Because, unlike a call
, callvirt
, or calli
, where the caller's stack frame would remain on the stack to be seen by future code access security stackwalks triggered (perhaps indirectly) by the callee, a jmp
instruction tears down the caller's stack frame prior to transitioning into the callee and is thus invisible to any CAS stackwalks that the callee may trigger.
Edit: I think naasking is right about the answer above being wrong. I now think the difference between (verifiable) tail.call sequences and (unverifiable) jmp sequences may be that a tail call requires pushing the arguments to the call onto the evaluation stack where they can be verified in the normal way, whereas a jmp
requires the evaluation stack to be empty and causes the jump-ee to inherit the arguments of the jump-er. There was probably no reason to complicate the verifier to check jmp
instructions, but it might be possible to do so under conditions similar to those imposed on tail.call
sequences (one of which is that the caller and callee must be in the same assembly, which rules out my CAS guess above, at least up to explicit .Deny( )
calls).
If so, this would be the relevant part of the spec: (Partition III, Section 3.37)
The current arguments are transferred
to the destination method.
The evaluation stack must be empty
when this instruction is executed. The
calling convention, number and type of
arguments at the destination address
must match that of the current method.