Repository navigation
Derive ilasm -DET identities from the assembly's content - #135227
Open
pcshrosbree wants to merge 2 commits into
Open
pcshrosbree wants to merge 2 commits into
pcshrosbree wants to merge 2 commits into
Conversation
ilasm computed the PdbChecksum debug directory entry over the #Pdb stream only, while PE-COFF.md requires the hash of the entire PDB file with its 20-byte PDB ID zeroed. Under -DET it also wrote only the deterministic GUID into the #Pdb stream, so the PDB ID's timestamp stayed 0 while the CodeView entry carried the deterministic stamp, and PEReader.TryOpenAssociatedPortablePdb rejected the pair. Compute the checksum by serializing the PDB with its ID temporarily zeroed, and write both the GUID and the timestamp into the #Pdb stream. Extend TestPortablePdbDebugDirectory to compare all 20 bytes of the PDB ID with the CodeView entry and to verify the PdbChecksum entry, with and without -DET.
Under -DET, ilasm derived the MVID, the PE timestamp and the PDB ID from SHA-256 of the metadata block straight after reserving it, before any metadata was written into it. The block was all zeros, so the identities depended only on the metadata's size, and different assemblies shared them. Derive them from the content instead, as PE-COFF.md describes: - The PDB ID is taken from the PdbChecksum, which is the hash of the serialized PDB with its ID zeroed. The CodeView entry carries that ID. - The MVID and the PE timestamp are taken from the hash of the whole image, computed once the image is complete, with both left zero. A new ICeeFileGen::ComputeImageHash fixes the image up and hashes it as GenerateCeeFile will write it, through PEWriter::write(void**), with ilasm supplying the hash function, since mscorpe has none. The MVID is then written into the already serialized metadata, and the timestamp into the file header. The in-memory writer, PEWriter::write(void**), was unreachable through the existing ICeeFileGen::GenerateCeeFile call path. It now sizes the image as write(fileName) writes the file, which with /STRIPRELOC includes the data of the stripped .reloc section; before, it would have written past the end of its buffer. Also fix a second -DET defect: the export directory's timestamp was the current time, so -DET output with exports was not repeatable. It is now 0, which also keeps it out of the MVID. Add TestDeterministicIdentity, which assembles pairs of inputs that differ only in a method body, in metadata of the same size or in sequence points, with and without -DEBUG, and checks that the identities differ where the content differs, that the same input gives the same bytes, and that each identity is the hash of its file with the identity fields zeroed. Add TestDeterministicImageLayout, which checks the same for /STRIPRELOC, PE32+ and an assembly with an export, and that the export timestamp is 0. The test helper now deletes earlier outputs before each run.
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @agocke |
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @dotnet/jit-contrib |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With
-DET, ilasm derives the MVID, the PE timestamp and the PDB ID from SHA-256 of the metadata block before any metadata has been written into it, so they depend only on the metadata's size. Different assemblies whose metadata is the same size share all three (#135214).Depends on #135212. Only the last commit is new; I'll rebase onto
mainonce #135212 merges.Change
-DETit is taken from thePdbChecksum, which Fix ilasm PdbChecksum and the PDB ID under -DET #135212 computes over the serialized PDB with its ID zeroed. The PDB is final apart from its ID at that point. PE-COFF.md describes this scheme, where one hash gives both the checksum and the ID. The CodeView entry carries this ID.DefineScopeand the current time no longer reach the output.CreatePEFile, a newICeeFileGen::ComputeImageHashfixes up the image and hashes it asGenerateCeeFilewill write it, throughPEWriter::write(void**). ilasm supplies the hash function, sincemscorpehas none. The hash covers the headers, the IL, the metadata, the resources and the debug directory.#GUIDstream.ICeeFileGen::GenerateCeeFilecall path. It now sizes the image the waywrite(fileName)sizes the file. With/STRIPRELOC, that includes the stripped.relocsection's data, whichwrite(fileName)still writes; before, the in-memory writer would have written past the end of its buffer.-DETdefect: the export directory's timestamp was the current time, so-DEToutput with exports was not repeatable. It is now 0, which also keeps it out of the MVID.TestDeterministicIdentityassembles pairs of inputs, with and without-DEBUG, from the same source path to the same output path. The pairs differ only in a method body, in metadata of the same size, or in sequence points. It checks that:TestDeterministicImageLayoutchecks repeatability and that hash rule for/STRIPRELOC, PE32+ and an assembly with an export. It also checks that the export timestamp is 0.Notes
-DEToutput changes. Only the identity fields and the export timestamp.-DEBUG, the DLLs differ in the COFF stamp, the MVID, the CodeView GUID, and the timestamps of the CodeView andPdbChecksumentries. The PDBs differ only in their ID. Without-DEBUG, only the COFF stamp and the MVID differ.-DETis unchanged: every change in ilasm is underm_fDeterministic, and the in-memory writer was not reached before.PortablePdbBuilderandPEBuilderalso use two hashes in that order./SUBSYSTEMand/BASEchange only the optional header, and with this change they give different MVIDs. Fixing up early is safe becausegenerateImageskipsfixup()once it has run, and nothing betweenCreatePEFileandGenerateCeeFilechanges the image apart from the MVID and the timestamp.-DEBUG: the MVID covers the absolute PDB path recorded in the CodeView entry. The IL round-trip determinism checks inCLRTest.Jit.targetsassemble to the same path twice, so they are unaffected..moduleis absent, and through the export directory.strncpy_sandstrcpy_sfill the bytes after the terminator: with0xFDon Linux (the PAL's), and with0xFEon Windows (the MSVC debug CRT's). The filled bytes are after section-header names such as.text,.rsrcand.reloc, and after the import DLL name; Release writes0x00there. Those bytes are in the file, so the hash covers them.ICeeFileGengains a virtual method at the end.mscorpeis a static library linked only into ilasm, which is its only consumer.BlobContentId.FromHashalso sets the GUID's version and variant bits and the timestamp's high bit; I can switch to it if you prefer.Validation (Linux x64 and Windows x64)
AI agents ran the builds, tests, mutation checks and probes on my machines; the results are summarized below.
ilasm_testsagainst a CheckedCore_Root, with counts fromilasm_tests.testResults.xml: 15 of 24 pass with Fix ilasm PdbChecksum and the PDB ID under -DET #135212 alone, where the 9 new cases fail, and 24 of 24 pass with this PR. Each of nine targeted mutants failed at least one new test case.TryOpenAssociatedPortablePdbsucceeds and thePdbChecksummatches the PE-COFF.md rule./DEBUG=OPT,/DEBUG=IMPL,/PDB, an EXE,/PE64 /X64,/STRIPRELOC, and an assembly with an export, with the two runs about 2 s apart. With Fix ilasm PdbChecksum and the PDB ID under -DET #135212 alone, the export case was not repeatable: its export timestamps were 1791211545 and 1791211547.*_ilasmroundtrip.py) passes over theilasm_testsoutput directory plusSystem.Collections,System.LinqandSystem.Private.CoreLib.ilasm_testsagainst a CheckedCore_Rootgives the same counts from the XML as on Linux: 15 of 24 with Fix ilasm PdbChecksum and the PDB ID under -DET #135212 alone and 24 of 24 with this PR. With both the Checked and the Release ilasm, the ilasm: -DET derives the MVID, PE timestamp and PDB ID from a zero-filled buffer, so different assemblies share them #135214 reproduction and the other switches above give the same results as on Linux, and each output's MVID, timestamp and PDB ID follow the hash rule the tests check. A Win32 resource (anrc.exeVERSIONINFO, converted withcvtres.exe, the form-RESOURCEreads) reaches the hash: changing one byte of it changes the MVID, which it did not with Fix ilasm PdbChecksum and the PDB ID under -DET #135212 alone. The IL round-trip and the mutants were not rerun on Windows.Not run locally: macOS, an Arm64 host or a big-endian target, and the full
src/testssuite. Arm64 and ARM output targets were tested from Linux x64. I can also run it on Windows ARM64 and Linux ARM64 hosts if that would help.Resolves #135214
Note
This change, its validation and this description were prepared with AI assistance (Anthropic Claude and OpenAI Codex) under my direction. AI agents ran the builds, tests and probes on my machines. I reviewed the diff and this text before posting.