[Mono-bugs] [Bug 51499][Blo] New - Problem with tail. instruction prefix (stack counting error)
bugzilla-daemon@bugzilla.ximian.com
bugzilla-daemon@bugzilla.ximian.com
Sat, 29 Nov 2003 12:51:22 -0500 (EST)
Please do not reply to this email- if you want to comment on the bug, go to the
URL shown below and enter your comments there.
Changed by kokholm@it-c.dk.
http://bugzilla.ximian.com/show_bug.cgi?id=51499
--- shadow/51499 2003-11-29 12:51:22.000000000 -0500
+++ shadow/51499.tmp.25323 2003-11-29 12:51:22.000000000 -0500
@@ -0,0 +1,202 @@
+Bug#: 51499
+Product: Mono/Runtime
+Version: unspecified
+OS:
+OS Details: XP Pro
+Status: NEW
+Resolution:
+Severity:
+Priority: Blocker
+Component: misc
+AssignedTo: mono-bugs@ximian.com
+ReportedBy: kokholm@it-c.dk
+QAContact: mono-bugs@ximian.com
+TargetMilestone: ---
+URL:
+Cc:
+Summary: Problem with tail. instruction prefix (stack counting error)
+
+Description of Problem:
+ This problem is a showstopper for running Moscow ML .Net
+ (http://www.dina.kvl.dl/~sestoft/mosml.html) on Mono v0.28.
+ Tried on WinXP Pro and Redhat 9.0.
+
+ Under some circumstances, a tail. instruction prefix in
+ a method body may make the JIT compiler emit eroneous code.
+ It seems to be an offset error in the compilation of ldarg.i
+ instructions and usually leads to unverifiable code or casting
+ errors. I have tried to extract a relatively simple example,
+ but I have only seen the problem with at least three assemblies
+ in play and the assembly with the tail. prefix loaded by
+ reflection.
+
+ In the following snippet from the example below,
+
+ ...
+ tail.
+ callvirt instance class [M]Value [M]MLClosure::Eval1(class [M]Value)
+ ret
+ THEEND:
+ ldarg.1
+ call class [M]Value [M]Value::printlna(class [M]Value)
+ ...
+
+ the final call will actually be called with argument0 (this) as
+ if the previous instruction was ldarg.0. Removing the tail. prefix
+ removes the problem. The problem also is somwhat dependent on the
+ precise code preceding the prefix.
+
+Steps to reproduce the problem:
+1. Save the code below into files A.il, M.cs and C.cs as indicated
+2. Compile the files: to A.dll, M.dll and C.exe:
+ ilasm /dll A.il
+ mcs /target:library M.cs
+ mcs /reference:M.dll C.cs
+3. Run C.exe:
+ mono C.exe
+ This erroneously writes
+ MLClosure(A.CL2)
+ to the terminal. Remove the tail. instruction prefix near the
+ end of A.il and repeat. Now mono C.exe writes
+ 7
+ to the terminal. Either version will write 7 to the terminal
+ when run using the MS .Net SDK runtime.
+
+------------A.il------------------------------------------------------
+.assembly extern mscorlib {}
+.assembly extern M{}
+.assembly A{}
+.module A.dll
+
+.namespace A
+{
+ .class public auto ansi main extends [mscorlib]System.Object
+ {
+ .field public static class [M]Value[] globals
+
+ .method public specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.s 0
+ call instance void [mscorlib]System.Object::.ctor()
+ ret
+ }
+
+ .method private specialname rtspecialname static
+ void .cctor() cil managed
+ {
+ ldc.i4 0x3
+ newarr [M]Value
+ stsfld class [M]Value[] A.main::globals
+ ret
+ }
+
+ .method public static void module_init() cil managed
+ {
+ ldsfld class [M]Value[] A.main::globals
+ ldc.i4 0x1
+ newobj instance void A.CL1::.ctor()
+ stelem.ref
+ ldsfld class [M]Value[] A.main::globals
+ ldc.i4 0x2
+ newobj instance void A.CL2::.ctor()
+ stelem.ref
+ ret
+ }
+
+ }
+
+ .class public auto ansi CL1 extends [M]MLClosure
+ {
+ .method public specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.s 0
+ call instance void [M]MLClosure::.ctor()
+ ret
+ }
+ .method public virtual instance class [M]Value
+ Eval1(class [M]Value A_1) cil managed
+ {
+ ldc.i4 0x1
+ newobj instance void [M]MLInt::.ctor(int32)
+ ret
+ }
+ }
+
+ .class public auto ansi CL2 extends [M]MLClosure
+ {
+ .method public specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.s 0
+ call instance void [M]MLClosure::.ctor()
+ ret
+ }
+ .method public virtual instance class [M]Value
+ Eval1(class [M]Value A_1) cil managed
+ {
+ ldc.i4 0x0
+ brfalse THEEND
+ ldsfld class [M]Value[] A.main::globals
+ ldc.i4 0x1
+ ldelem.ref
+ castclass [M]MLClosure
+ ldc.i4 0x0
+ newobj instance void [M]MLInt::.ctor(int32)
+ tail.
+ callvirt instance class [M]Value [M]MLClosure::Eval1(class [M]
+Value)
+ ret
+ THEEND:
+ ldarg.1
+ call class [M]Value [M]Value::printlna(class [M]Value)
+ ret
+ }
+ }
+}
+-----------------------------------------------------------------------
+----------------M.cs---------------------------------------------------
+public abstract class Value {
+ public static Value printlna(Value v) {
+ System.Console.WriteLine(v.ToString());
+ return new MLInt(0);
+ }
+}
+
+public class MLInt : Value {
+ public int val;
+ public MLInt(int i) {
+ val = i;
+ }
+ public override string ToString() {
+ return val.ToString();
+ }
+}
+
+public abstract class MLClosure : Value {
+ public abstract Value Eval1(Value v);
+ public override string ToString() {
+ return "MLClosure("+GetType()+")";
+ }
+}
+---------------------------------------------------------------
+-----------------C.cs-------------------------------------------
+using System;
+using System.Reflection;
+public class T_smlcall {
+ public static void Main(string[] args) {
+ Assembly assem = Assembly.LoadFrom("A.dll");
+ Type clinf = assem.GetType("A.main");
+ ConstructorInfo constructor = clinf.GetConstructor(new System.Type
+[0]);
+ object main = constructor.Invoke(new System.Object[0]);
+ MethodInfo initMethod = clinf.GetMethod("module_init");
+ initMethod.Invoke(main,new System.Object[] {});
+ Value[] vals = (Value[])(clinf.GetField("globals").GetValue(main));
+ Value fac = vals[2];
+ Value seven = new MLInt(7);
+ Value mlretval = ((MLClosure)fac).Eval1(seven);
+ }
+}
+-------------------------------------------------------------------------