Skip to content

Instantly share code, notes, and snippets.

@xoofx
Created December 11, 2019 17:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xoofx/c4a9777eaafb47ca70ff30c64f314137 to your computer and use it in GitHub Desktop.
Save xoofx/c4a9777eaafb47ca70ff30c64f314137 to your computer and use it in GitHub Desktop.
Example of the ldflda opcode crashing on Mono (5.21 and 6.4.0)
// Create a .NET Core project, target also net472
// Requires to install NuGet package InlineIL.Fody https://github.com/ltrzesniewski/InlineIL.Fody in your project
// Run this program
using System;
using InlineIL;
namespace PlayIL
{
class Program
{
// The following program will generate a crash in Mono (Tested on Mono 5.21 and 6.4.0)
//
// CheckLdFldLdFlda
// Unhandled Exception:
// System.InvalidProgramException: Invalid IL code in PlayIL.Program:CheckLdFldLdFlda (PlayIL.Program/RootStruct): IL_0006: ldflda 0x0a000004
// at PlayIL.Program.Main (System.String[] args) [0x0004b] in <5cd1d0b45a8f4b03b56221d0e2065d86>:0
// [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidProgramException: Invalid IL code in PlayIL.Program:CheckLdFldLdFlda (PlayIL.Program/RootStruct): IL_0006: ldflda 0x0a000004
// at PlayIL.Program.Main (System.String[] args) [0x0004b] in <5cd1d0b45a8f4b03b56221d0e2065d86>:0
//
// As ldflda is not supported if the input this "pointer" is actually not a pointer but a value on the stack
// While this is supported by .NET CLR and also according to the specs
static void Main(string[] args)
{
Console.WriteLine("CheckLdFldLdFlda");
var rootStruct = new RootStruct() { Nested = new NestedStruct() { SubNested = new SubNestedStruct() { SubSubNested = new SubSubNestedStruct() { Value = 10 } } } };
Console.WriteLine(CheckLdFldLdFlda(rootStruct));
// Comment these on Mono
Console.WriteLine("CheckLdFldLdFldaWithCall");
rootStruct = new RootStruct() { Nested = new NestedStruct() { SubNested = new SubNestedStruct() { SubSubNested = new SubSubNestedStruct() { Value = 11 } } } };
Console.WriteLine(CheckLdFldLdFldaWithCall(rootStruct));
// Comment these on Mono
Console.WriteLine("CheckLdFldLdFldaWithLdElem");
Console.WriteLine(CheckLdFldLdFldaWithLdElem());
}
public static int CheckLdFldLdFlda(RootStruct rootStruct)
{
IL.Emit.Ldarg(nameof(rootStruct));
IL.Emit.Ldfld(new FieldRef(typeof(RootStruct), nameof(RootStruct.Nested)));
// ldflda taking directly a NestedStruct instance on the evaluation stack, not a pointer
IL.Emit.Ldflda(new FieldRef(typeof(NestedStruct), nameof(NestedStruct.SubNested)));
IL.Emit.Ldfld(new FieldRef(typeof(SubNestedStruct), nameof(SubNestedStruct.SubSubNested)));
IL.Emit.Ldfld(new FieldRef(typeof(SubSubNestedStruct), nameof(SubSubNestedStruct.Value)));
return IL.Return<int>();
}
public static int CheckLdFldLdFldaWithCall(RootStruct rootStruct)
{
IL.Emit.Ldarg(nameof(rootStruct));
IL.Emit.Ldfld(new FieldRef(typeof(RootStruct), nameof(RootStruct.Nested)));
IL.Emit.Call(new MethodRef(new TypeRef(typeof(Program)), nameof(NestedStructGoThrough)));
IL.Emit.Ldfld(new FieldRef(typeof(NestedStruct), nameof(NestedStruct.SubNested)));
// ldflda taking directly a NestedStruct instance on the evaluation stack, not a pointer
IL.Emit.Ldflda(new FieldRef(typeof(SubNestedStruct), nameof(SubNestedStruct.SubSubNested)));
IL.Emit.Ldfld(new FieldRef(typeof(SubSubNestedStruct), nameof(SubSubNestedStruct.Value)));
return IL.Return<int>();
}
public static int CheckLdFldLdFldaWithLdElem()
{
IL.Emit.Ldsfld(new FieldRef(typeof(RootStruct), nameof(RootStruct.RootStructArray)));
IL.Emit.Ldc_I4_1();
IL.Emit.Ldelem_Any(new TypeRef(typeof(RootStruct)));
IL.Emit.Ldfld(new FieldRef(typeof(RootStruct), nameof(RootStruct.Nested)));
// ldflda taking directly a NestedStruct instance on the evaluation stack, not a pointer
IL.Emit.Ldflda(new FieldRef(typeof(NestedStruct), nameof(NestedStruct.SubNested)));
IL.Emit.Ldfld(new FieldRef(typeof(SubNestedStruct), nameof(SubNestedStruct.SubSubNested)));
IL.Emit.Ldfld(new FieldRef(typeof(SubSubNestedStruct), nameof(SubSubNestedStruct.Value)));
return IL.Return<int>();
}
private static SubNestedStruct NestedStructGoThrough(NestedStruct nested)
{
return nested.SubNested;
}
public struct RootStruct
{
public static readonly RootStruct[] RootStructArray = new RootStruct[]
{
new RootStruct(),
new RootStruct() {Nested = new NestedStruct() {SubNested = new SubNestedStruct() {SubSubNested = new SubSubNestedStruct() {Value = 12}}}}
};
public NestedStruct Nested;
}
public struct NestedStruct
{
public SubNestedStruct SubNested;
}
public struct SubNestedStruct
{
public SubSubNestedStruct SubSubNested;
}
public struct SubSubNestedStruct
{
public int Value;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment