diff options
author | Sven Boemer <sbomer@gmail.com> | 2022-08-12 20:09:01 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-12 20:09:01 +0300 |
commit | d559347987ba45c0dc3389cb96dce1ae20204513 (patch) | |
tree | 9fd6b098611a5ac0ef3a2e5a74fdfda8b7948211 /src | |
parent | 95ea1842abc8634d1e98d995577c4202ad3fc5c5 (diff) |
Fix il corruption (#2966)
This prevents invalid IL in situations where removing the last
instruction of a method would result in the last instruction
being a conditional branch. There needs to be some IL in the
not-taken branch for the IL to be valid, so this fixes the issue
by injecting "ldnull; throw" at the end.
Co-authored-by: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
Co-authored-by: Adam Sitnik <adam.sitnik@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs index 8e8fda213..d91fb09dd 100644 --- a/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs +++ b/src/linker/Linker.Steps/UnreachableBlocksOptimizer.cs @@ -806,6 +806,9 @@ namespace Mono.Linker.Steps return changed; } + static bool IsConditionalBranch (OpCode opCode) + => opCode.Code is Code.Brfalse or Code.Brfalse_S or Code.Brtrue or Code.Brtrue_S; + void RemoveUnreachableInstructions (BitArray reachable) { LinkerILProcessor processor = Body.GetLinkerILProcessor (); @@ -815,8 +818,22 @@ namespace Mono.Linker.Steps if (reachable[i]) continue; - processor.RemoveAt (i - removed); - ++removed; + int index = i - removed; + // If we intend to remove the last instruction we replaced it with "ret" above (not "nop") + // but we can't get rid of it completely because it may happen that the last kept instruction + // is a conditional branch - in which case to keep the IL valid, there has to be something after + // the conditional branch instruction (the else branch). So if that's the case + // inject "ldnull; throw;" at the end - this branch should never be reachable and it's always valid + // (ret may need to return a value of the right type if the method has a return value which is complicated + // to construct out of nothing). + if (index == Body.Instructions.Count - 1 && Body.Instructions[index].OpCode == OpCodes.Ret && + index > 0 && IsConditionalBranch (Body.Instructions[index - 1].OpCode)) { + processor.Replace (index, Instruction.Create (OpCodes.Ldnull)); + processor.InsertAfter (Body.Instructions[index], Instruction.Create (OpCodes.Throw)); + } else { + processor.RemoveAt (index); + ++removed; + } } } |