Ответ 1

Потому что, в отличие от call, callvirt или calli, где фрейм стека вызывающего абонента останется в стеке, который будет рассматриваться будущими защитными стеками безопасности доступа к коду, вызванным (возможно, косвенно) вызываемым, a jmp срывает кадр стека вызывающего абонента перед тем, как перейти к вызываемому абоненту и, таким образом, невидимо для любых стеков стека CAS, которые вызывающий может вызвать.

Редактирование: я считаю, что naasking прав насчет неправильного ответа. Теперь я думаю, что разница между (проверяемыми) tail.call последовательностями и (непроверяемыми) jmp-последовательностями может заключаться в том, что хвостовой вызов требует нажатие аргументов на вызов в стек оценки, где они могут быть проверены обычным способом, тогда как a jmp требует, чтобы стек оценки был пустым и заставлял jump-ee наследовать аргументы jump-er. Вероятно, не было причины усложнять верификатор для проверки инструкций jmp, но это может быть возможно в условиях, аналогичных тем, которые накладываются на последовательности tail.call (один из которых заключается в том, что вызывающий и вызываемый должны быть в одном и том же сборка, которая исключает мое предположение CAS выше, по крайней мере, до явных вызовов .Deny( )).

Если это так, это будет соответствующей частью спецификации: (раздел III, раздел 3.37)

Текущие аргументы передаются к методу назначения.

Стол оценки должен быть пустым когда эта инструкция выполнена. вызов, количество и тип аргументы по адресу назначения должен соответствовать текущему методу.