Два дня гуглил — ничего не нагуглил.
Берём примитивный тестовый код:
public static void Test()
{
unsafe
{
fixed (int* p = new int[1])
{
*p = -1;
}
}
}
Всё работает. Смотрим в ILDASM:
.method /*06000013*/ public hidebysig static void
Test() cil managed
{
.maxstack 2
.locals /*1100000D*/ init (
[0] int32* p,
[1] int32[] pinned V_1
)
IL_0000: nop
IL_0001: nop
IL_0002: ldc.i4.1
IL_0003: newarr [mscorlib/*23000001*/]System.Int32/*0100003F*/
IL_0008: dup
IL_0009: stloc.1 // V_1
IL_000a: brfalse.s IL_0011
IL_000c: ldloc.1 // V_1
IL_000d: ldlen
IL_000e: conv.i4
IL_000f: brtrue.s IL_0016
IL_0011: ldc.i4.0
IL_0012: conv.u
IL_0013: stloc.0 // p
IL_0014: br.s IL_001f
IL_0016: ldloc.1 // V_1
IL_0017: ldc.i4.0
IL_0018: ldelema [mscorlib/*23000001*/]System.Int32/*0100003F*/
IL_001d: conv.u
IL_001e: stloc.0 // p
IL_001f: nop
IL_0020: ldloc.0 // p
IL_0021: ldc.i4.m1
IL_0022: stind.i4
IL_0023: nop
IL_0024: ldnull
IL_0025: stloc.1 // V_1
IL_0026: nop
IL_0027: ret
} // end of method Program::Test
Ничего военного — основные приседания вокруг пиннинга массива и проверки, что он не null и длина больше нуля.
Пробуем воспроизвести:
private static void GenerateTestMSIL(ILGenerator ilg)
{
var p = ilg.DeclareLocal(typeof(int*));
var t = ilg.DeclareLocal(typeof(int[]), true);
var zeroPtr = ilg.DefineLabel();
var nonZeroPtr = ilg.DefineLabel();
var main = ilg.DefineLabel();
ilg.Emit(OpCodes.Ldc_I4_1);
ilg.Emit(OpCodes.Newarr, typeof(int));
//ilg.Emit(OpCodes.Dup);
ilg.Emit(OpCodes.Stloc, t);
//ilg.Emit(OpCodes.Brfalse, zeroPtr);//
//ilg.Emit(OpCodes.Ldloc, t);
//ilg.Emit(OpCodes.Ldlen);
//ilg.Emit(OpCodes.Conv_I4);
//ilg.Emit(OpCodes.Brtrue, nonZeroPtr);
//ilg.MarkLabel(zeroPtr);
//ilg.Emit(OpCodes.Ldc_I4_0);
//ilg.Emit(OpCodes.Stloc, p);
//ilg.Emit(OpCodes.Br_S, main);
ilg.MarkLabel(nonZeroPtr);
ilg.Emit(OpCodes.Ldloc, t);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ldelema, typeof(int));
ilg.Emit(OpCodes.Conv_U);
ilg.Emit(OpCodes.Stloc, p);
ilg.MarkLabel(main);
ilg.Emit(OpCodes.Ldloc, p);
ilg.Emit(OpCodes.Ldc_I4_M1);
ilg.Emit(OpCodes.Stind_I4);
//ilg.Emit(OpCodes.Ldnull);
//ilg.Emit(OpCodes.Stloc, t);
ilg.Emit(OpCodes.Ret);
}
(Закомментированы избыточные операции, которые C# вставляет из паранойи — их наличие, к сожалению, на работоспособность не влияет).
Вот теперь странности:
1. Если мы генерируем этот код в метод класса, созданного в рамках динамической сборки, то всё работает, как ожидается:
public static Action GenerateTestAssembly()
{
var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("FixedTest"), AssemblyBuilderAccess.Run);
var tb = ab.DefineDynamicModule("FixedTest", "FixedTest.dll").DefineType("FixedTest", TypeAttributes.Class | TypeAttributes.Public);
var mtb = tb.DefineMethod("Test", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static);
GenerateTestMSIL(mtb.GetILGenerator());
Type type = tb.CreateType();
return (Action)type.GetMethod("Test").CreateDelegate(typeof(Action));
}
А вот если мы попробуем такой же MSIL сгенерировать в рамках DynamicMethod, то попытка вызвать его приведёт к InvalidOperationException "This operation could destabilize the runtime".
В чём может быть дело?
Понятное дело, что PEVerify ругается на порождённый таким образом код, точно так же, как и на оригинальный метод Test(). Поэтому найти, что именно там could destabilize the runtime, не представляется возможным.
Здравствуйте, Sinclair, Вы писали:
S>А вот если мы попробуем такой же MSIL сгенерировать в рамках DynamicMethod, то попытка вызвать его приведёт к InvalidOperationException "This operation could destabilize the runtime".
S>В чём может быть дело?
код выглядит нормально,
ilg.Emit(OpCodes.Ldc_I4_1); // 1 1
ilg.Emit(OpCodes.Newarr, typeof(int)); // 1 int[1]
ilg.Emit(OpCodes.Stloc, t); // 0
ilg.Emit(OpCodes.Ldloc, t); // 1 int[1]
ilg.Emit(OpCodes.Ldc_I4_0); // 2 int[1], 0
ilg.Emit(OpCodes.Ldelema, typeof(int)); // 1 &int[0]
ilg.Emit(OpCodes.Conv_U); // 1 &int[0]
ilg.Emit(OpCodes.Stloc, p); // 0
ilg.Emit(OpCodes.Ldloc, p); // 1 &int[0]
ilg.Emit(OpCodes.Ldc_I4_M1); // 2 &int[0], -1
ilg.Emit(OpCodes.Stind_I4); // 0
ilg.Emit(OpCodes.Ret);
}
Похоже, что дело в permission-ах. Долгое объяснение —
https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/security-issues-in-reflection-emit
Я не помню точно, есть ли способ отрубить все проверки, но может помочь параметр restrictedSkipVisibility + привязка к модулю в конструкторе DynamicMethod.
Также см
https://www.codeproject.com/Answers/494914/Can-tplusfigureplusoutpluswhyplusthisplusDynamic#answer2
https://stackoverflow.com/a/13432022
https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.dynamicmethod?view=netframework-4.7.2
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Sinclair, Вы писали:
S>>А вот если мы попробуем такой же MSIL сгенерировать в рамках DynamicMethod, то попытка вызвать его приведёт к InvalidOperationException "This operation could destabilize the runtime".
S>>В чём может быть дело?
S>код выглядит нормально,
Более того — он
работает. В рамках нормального типа в Dynamic Assembly.
S>Похоже, что дело в permission-ах. Долгое объяснение — https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/security-issues-in-reflection-emit
S>Я не помню точно, есть ли способ отрубить все проверки, но может помочь параметр restrictedSkipVisibility + привязка к модулю в конструкторе DynamicMethod.
Попробуем.
S>Также см
S>https://www.codeproject.com/Answers/494914/Can-tplusfigureplusoutpluswhyplusthisplusDynamic#answer2
S>https://stackoverflow.com/a/13432022
S>https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.dynamicmethod?view=netframework-4.7.2
Спасибо.
Здравствуйте, Sinclair, Вы писали:
S>Попробуем.
Помогло?
Здравствуйте, Sinix, Вы писали:
S>Помогло?
Да. Нужна именно привязка к модулю. skipVisibility не нужен — что и ожидалось, я ж ни в какие приваты не лезу.