如何在C#/ IL中突变盒装值类型(原始或结构) [英] How to mutate a boxed value type (primitive or struct) in C#/IL

查看:159
本文介绍了如何在C#/ IL中突变盒装值类型(原始或结构)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用IL突变盒装结构我试图以常规方式更改盒装值类型的值,所以尝试实现以下方法:

Related to How to mutate a boxed struct using IL I am trying to change the value of a boxed value type but in a generic way, so trying to implement the following method:

void MutateValueType<T>(object o, T v) where T : struct

应该是可能的:

var oi = (object)17;
MutateValueType<int>(oi, 43);
Console.WriteLine(oi); // 43

var od = (object)17.7d;
MutateValueType<double>(od, 42.3);
Console.WriteLine(od); // 42.3

我无法在.NET Framework上使用它(请参阅@的评论)因此,没有 typeof(Program).Module 的实现可在其他运行时上使用)。我已经实现了这一点,如下所示。但是,当使用以下命令调用委托 del 时失败:

I am failing to get it this to work on .NET Framework (see comment by @hvd that the implementation without typeof(Program).Module works on other runtimes). I have implemented this as seen below. However, this fails when calling the delegate del with a:

System.Security.VerificationException: 'Operation could destabilize the runtime.'

这是我提出的实现

public static void MutateValueType<T>(object o, T v)
{
    var dynMtd = new DynamicMethod("EvilMutateValueType", 
        typeof(void), new Type[] { typeof(object), typeof(T) });
    var il = dynMtd.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);           // object
    il.Emit(OpCodes.Unbox, typeof(T));  // T&
    il.Emit(OpCodes.Ldarg_1);           // T (argument value)
    il.Emit(OpCodes.Stobj, typeof(T));  // stobj !!T
    il.Emit(OpCodes.Ret);               

    var del = (Action<object, T>)dynMtd.CreateDelegate(typeof(Action<object, T>));
    del(o, v);
}

以上应该等同于下面的IL,这是可行的,但仍然

The above should be equivalent to the below IL, that works, but still the above fails, so the question is why this doesn't work.

  .method public hidebysig static void Mutate<T>(object o, !!T Value) cil managed aggressiveinlining
  {
    .maxstack  2
    ldarg.0
    unbox !!T
    ldarg.1
    stobj !!T
    ret
  }


推荐答案

不同之处在于,默认情况下 DynamicMethod 需要可验证的代码,而默认情况下,您自己的代码(包括自定义IL)则不可验证。

The difference is that DynamicMethod by default requires verifiable code, whereas your own code (including custom IL) is by default allowed to be unverifiable.

您可以将 DynamicMethod 视为自己模块的一部分,通过指定以下模块,使其包含不可验证的IL:

You can treat the DynamicMethod as part of your own module, allowing it to contain unverifiable IL, by specifying the module:

var dynMtd = new DynamicMethod("EvilMutateValueType",
    typeof(void), new Type[] { typeof(object), typeof(T) }, typeof(Program).Module);
// Use whatever class you have available here.              ^^^^^^^^^^^^^^^^^^^^^^

尽管PEVerify中存在其他一些问题,但难以获得良好的诊断,看来这至少是不可验证的:

Despite some other issues in PEVerify making it hard to get good diagnostics, it looks like this is intended to at least not be verifiable:


III.1.8.1.2.2受控可变性托管指针

只读。前缀和 unbox 指令可以生成称为受控可变性托管指针的内容。与普通托管指针类型不同,受控可变性托管指针不是 verifier-assignable-to (第III.1.8.1.2.3节)普通托管指针;例如,不能将
作为byref参数传递给方法。在控制流点,可以将受控可变性托管指针与相同类型的托管指针合并,以产生受控可变性托管指针。

The readonly. prefix and unbox instructions can produce what is called a controlled-mutability managed pointer. Unlike ordinary managed pointer types, a controlled-mutability managed pointer is not verifier-assignable-to (§III.1.8.1.2.3) ordinary managed pointers; e.g., it cannot be passed as a byref argument to a method. At control flow points, a controlled-mutability managed pointer can be merged with a managed pointer of the same type to yield a controlled-mutability managed pointer.

受控可变性托管指针指针只能通过以下方式使用:

Controlled-mutability managed pointers can only be used in the following ways:


  1. 作为 ldfld ldflda stfld 通话 callvirt 受限。 callvirt 指令。

  2. 作为 ldind的指针参数。* ldobj 指令。

  3. 作为 cpobj 指令的源参数。

  1. As the object parameter for an ldfld, ldflda, stfld, call, callvirt, or constrained. callvirt instruction.
  2. As the pointer parameter to a ldind.* or ldobj instruction.
  3. As the source parameter to a cpobj instruction.

所有其他操作(包括 stobj stind。* initobj mkrefany )无效。

All other operations (including stobj, stind.*, initobj, and mkrefany) are invalid.

[...]

但是看起来它仍然是正确的:

But it looks like it's intended to still be correct:


III.4.29 stobj –将值存储在地址中

[... ]

正确性:

Correctness:

正确的CIL确保 dest 是指向 T 的指针,而 src 的类型是 verifier-assignable-to T

Correct CIL ensures that dest is a pointer to T and the type of src is verifier-assignable-to T.

[...]

请注意,对受控可变性托管指针没有限制在这里,任何指向 T 的指针都是允许的。

Note that there is no restriction on controlled-mutability managed pointers here, any pointer to T is allowed.

因此,确保没有对您的IL进行验证是正确的方式。

Therefore, ensuring that no verification happens for your IL is the right way to go.

这篇关于如何在C#/ IL中突变盒装值类型(原始或结构)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆