Repository navigation
ARM64/ELF: Inline single-precision literal pool reads - #614
Closed
nichinichisou0609 wants to merge 1 commit into
Closed
nichinichisou0609 wants to merge 1 commit into
nichinichisou0609 wants to merge 1 commit into
Conversation
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.
Problem
On arm64 a float constant that does not fit an immediate is emitted into a literal pool and loaded via
adrp/ldr:NewArmV8InstructionSetalready folds that pair into an absolute address (theadrpOffsetspath), so what reachesIlGeneratoris aMemoryOperandwithIsConstant == true. But theMemoryOperandcase there only handles a byref dereference, so every literal pool read degrades to a diagnostic plus a zero:The value is lost, and since the pushed zero often has the wrong stack type, ILSpy then reports type mismatches around it.
Change
Three files, +59/-0.
Il2CppBinary.IsVirtualAddressReadOnly(ulong)— new virtual predicate defaulting tofalse, the same conservative shape as the neighbouringIsExportedFunction. A format only answers true once it actually reports segment permissions.ElfFileoverrides it: the address must lie in aPT_LOADsegment withPF_Wclear, and belowRawSize— bytes past that are zero-filled at load time rather than read from the file, so they are not constants.NewArmV8InstructionSet, in the LDR case: when the destination register isS0–S31and the source is a constant address in a read-only segment, the operand becomes aFloatLiteralinstead of aMemoryOperand.Producing the literal in the ISIL layer rather than at IL emission means
IlGenerator's existingcase FloatLiteralhandles it with no change, and the analysis passes get to see a constant as well.Why only
SnAn
LDR Dnmay be supplying two.2Slanes rather than one double, so reading 8 bytes there can fabricate a value out of two adjacent floats.Snhas an unambiguous width.This is not hypothetical: an earlier revision of this patch took the width from the expected type instead of the destination register, and produced doubles such as
7.032822090631087E+19— whose bit pattern is exactly(57.2958f, 570f), i.e.180/πnext to its neighbour in the pool.Effect
Two different Unity 2022.3 arm64 ELF binaries,
--output-as dll_il_recovery:[Fail]Recovery rate and failure count are unchanged, so the change is additive. Within one assembly of binary A, the decompiled output loses 463
Unmanaged memory loadnotes and 223Expected X, but got Yremarks, andInvalid comparison between …drops from 150 to 64.Spot checks on recovered values: a UI fade path that decompiled as
DOFade(0f, 0f)now readsDOFade(0f, 0.2f); recovered constants include3.1415927410125732, which is(double)(float)πand so confirms a 4-byte read rather than an 8-byte one, and57.29578f.Cpp2IL.Core.Tests(76) andLibCpp2ILTests(5) pass unchanged.Not covered
falseand are unaffected. The same ISIL shape exists on x86 —X86InstructionSetemitsMemoryOperand(addend: IPRelativeMemoryAddress)for RIP-relative operands — so the predicate is reusable there, but I have no x86 sample to verify against and would rather not add an unexercised path.LDR Dnis deliberately left alone, for the width reason above.GameAssembly.dll), so there is no arm64 ELF fixture to write a unit test against. Glad to add one if you would want such a sample included.