Some low level surgery on F#

It’s been a bit annoying, not being able to try out the F# web tools as I documented here. At the weekend, I decided to jump in and see if there was anything more I could do. Note that I am working here with the version of the F# code base, and I’m not even sure if this is the right solution. It seems to get me further though.
After downloading the F# web tools and making the necessary changes to the configuration, I was at the stage of getting the following error.
Exception Details: System.Reflection.AmbiguousMatchException: Ambiguous match found.
Source Error:
Line 101:                   if (mi.MemberType = MemberTypes.Constructor) then Seq.empty else
Line 102:                     (mi.GetGenericArguments() |> Array.to_seq |> ( fun _ -> objType ) ); ]) |> Seq.to_list;
Line 103:    let defn = ResolveTopDefinition (modDef, genArgs) in
Line 104:    let defn = if defn = None then ResolveTopDefinition (modDef2, genArgs) else defn in
Line 105:   
Source File: C:fswebtoolssourceWebTools.Coreweb_asp.fs    Line: 103
Stack Trace:
[AmbiguousMatchException: Ambiguous match found.]
   System.DefaultBinder.SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) +2798322
   System.RuntimeType.GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) +134
   System.Type.GetConstructor(BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) +63
   Microsoft.FSharp.Quotations.Raw.bindCtor(Tuple`2 _arg44, List`1 tyargs) +90
As the F# release comes with source code, I could see that the GetConstructor is being called in the quotations.fs file in the bindCtor function.
    let bindCtor ((tc,argTypes : (Type list tyenv)),tyargs) =
        let typ = MkNamedType(tc,tyargs)
        let argtyps = argTypes (List.nth tyargs)
        typ.GetConstructor(bindingFlags,null,Array.of_list argtyps,null) |> nonNull ("bindCtor: failed to bind constructor")
It is passing the binding flags:
    let bindingFlags = BindingFlags.Instance ||| BindingFlags.Static ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
The code in question is trying to regenerate the top level reflected definitions for the code in the WebTools.Controls module. These top level definitions are stored as resources within the assembly. When a call is made to fetch the reflected definitions, they are reloaded from the resource stream and cached. The only odd thing is that the system is using BindingFlags.Static when reflecting over the constructors of a type.
I was running on Vista, so I called up windbg, running it as administrator, and connected to the w3wp.exe ASP.NET worker process. We now need to find and breakpoint the correct overload of GetConstructor. This is relatively straight forward using the SOS debugger extensions that come as part of the framework.
We load up the sos extensions and find the method table for the System.Type type.
0:023> .loadby sos mscorwks
0:023> !Name2EE *!System.Type
Module: 790c2000 (mscorlib.dll)
Token: 0x020000f1
MethodTable: 79106894
EEClass: 79106824
Name: System.Type
We can then dump the method table to find the overload that interests us.
0:023> !DumpMT -MD 79106894
793a04d4   7925e390   PreJIT System.Type.GetConstructor(System.Reflection.BindingFlags, System.Reflection.Binder, System.Type[], System.Reflection.ParameterModifier[])
And we can get the details about the method including its code.
0:023> !DumpMD 7925e390  
Method Name: System.Type.GetConstructor(System.Reflection.BindingFlags, System.Reflection.Binder, System.Type[], System.Reflection.ParameterModifier[])
Class: 79106824
MethodTable: 79106894
mdToken: 06000d50
Module: 790c2000
IsJitted: yes
m_CodeOrIL: 793a04d4
We can disassemble to look at the function.
0:023> u 793a04d4
793a04d4 57              push    edi
793a04d5 56              push    esi
793a04d6 53              push    ebx
793a04d7 55              push    ebp
793a04d8 8b5c241c        mov     ebx,dword ptr [esp+1Ch]
793a04dc 8b7c2418        mov     edi,dword ptr [esp+18h]
793a04e0 8b6c2414        mov     ebp,dword ptr [esp+14h]
793a04e4 85ff            test    edi,edi
And then set a breakpoint on the first instruction.
0:023> bp 793a04d4
We can then run until we get to a point where the backtrace looks like that of the ASP.NET error page.
0:023> g
Breakpoint 0 hit
eax=05c4d960 ebx=05902050 ecx=05902050 edx=0000003e esi=05a57db4 edi=0197f9d4
eip=793a04d4 esp=0fa4e8a0 ebp=0fa4e8bc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
793a04d4 57              push    edi
0:021> !ClrStack
OS Thread Id: 0x1cbc (21)
ESP       EIP    
0fa4e8a0 793a04d4 System.Type.GetConstructor(System.Reflection.BindingFlags, System.Reflection.Binder, System.Type[], System.Reflection.ParameterModifier[])
0fa4e8b0 0ea7f4b7 Microsoft.FSharp.Quotations.Raw.bindCtor(Microsoft.FSharp.Core.Tuple`2<System.Type,Microsoft.FSharp.Core.FastFunc`2<Microsoft.FSharp.Core.FastFunc`2<Int32,System.Type>,Microsoft.FSharp.Collections.List`1<System.Type>>>, Microsoft.FSharp.Collections.List`1<System.Type>)
0fa4e8c4 0ea87f5e Microsoft.FSharp.Quotations.Raw+u_constSpec@1155_1.Invoke(Microsoft.FSharp.Collections.List`1<System.Type>)
0fa4e8d4 0ea89e3f Microsoft.FSharp.Quotations.Raw+u_expr@1110_2.Invoke(Microsoft.FSharp.Core.FastFunc`2<Int32,System.Type>)
0fa4e8ec 0ead27b1[[System.__Canon, mscorlib],[System.__Canon, mscorlib]](Microsoft.FSharp.Core.FastFunc`2<System.__Canon,System.__Canon>, Microsoft.FSharp.Collections.List`1<System.__Canon>)
0fa4e948 0ea89e67 Microsoft.FSharp.Quotations.Raw+u_expr@1110_2.Invoke(Microsoft.FSharp.Core.FastFunc`2<Int32,System.Type>)
0fa4e960 0ea8772e Microsoft.FSharp.Quotations.Raw+u_expr@1115.Invoke(Microsoft.FSharp.Core.FastFunc`2<Int32,System.Type>)
0fa4e974 0ead28f2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]](Microsoft.FSharp.Core.FastFunc`2<System.__Canon,System.__Canon>, Microsoft.FSharp.Collections.List`1<System.__Canon>)
We find it is the sixth call which fails with the following stack. We can use -a to look at the addresses of the arguments.
0:021> !ClrStack -a
OS Thread Id: 0x1cbc (21)
ESP       EIP    
0fa4e530 793a04d4 System.Type.GetConstructor(System.Reflection.BindingFlags, System.Reflection.Binder, System.Type[], System.Reflection.ParameterModifier[])
        this = 0x05999fd0
        bindingAttr = 0x0000003e
        binder = 0x00000000
        types = 0x05c39708
        modifiers = 0x00000000
        <no data>
We can look at the this point to check that it looks relevant.
0:021> !DumpObj 0x05999fd0
Name: System.RuntimeType
MethodTable: 790ff734
EEClass: 790ff6c4
Size: 20(0x14) bytes
Type MethodTable: 7912f0c0
Type Name: System.Collections.Generic.List`1[[System.Object, mscorlib]]
When I did this the first time, I simply tried changing the BindingFlags which I found in the edx register from 0x3e to 0x37 to get rid of the Static binding flag. That worked, so now we’ll patch the generated code to pass this instead of the original.
We dump the stack to find the return address.
0:021> dd esp
0fa4e530  0ea7f4b7 00000000 05c39708 00000000
0fa4e540  05c3963c 05c3967c 05c3966c 05c3963c
0fa4e550  0ea87f5e 05c389c8 0195d840 05c3965c
0fa4e560  0ea89e3f 0fa4e574 0edbd71c 0ed13c24
0fa4e570  0ee8f780 0195d858 0ead28f2 0ed13c24
0fa4e580  0ed13c24 0edbd718 0edbd714 0ed13c24
0fa4e590  0ed13c24 0edbd718 0ed5c784 05953128
0fa4e5a0  05c3952c 0edbd714 05c3953c 0195d858
0:021> u 0ea7f4b7-20
0ea7f497 0e              push    cs
0ea7f498 8b30            mov     esi,dword ptr [eax]
0ea7f49a 6a00            push    0
0ea7f49c ba60ffe70e      mov     edx,offset FSharp_Core_ni+0x43ff60 (0ee7ff60)
0ea7f4a1 e8c2ec0300      call    FSharp_Core_ni+0x7e168 (0eabe168)
0ea7f4a6 50              push    eax
0ea7f4a7 6a00            push    0
0ea7f4a9 8bcb            mov     ecx,ebx
0:021> u
0ea7f4ab ba3e000000      mov     edx,3Eh
0ea7f4b0 3909            cmp     dword ptr [ecx],ecx
0ea7f4b2 e81d10926a      call    mscorlib_ni+0x2e04d4 (793a04d4)
0ea7f4b7 8bd0            mov     edx,eax
0ea7f4b9 684849e80e      push    offset FSharp_Core_ni+0x444948 (0ee84948)
0ea7f4be 8bce            mov     ecx,esi
0ea7f4c0 b838f3a70e      mov     eax,offset FSharp_Core_ni+0x3f338 (0ea7f338)
0ea7f4c5 6a00            push    0
We can now see the load of 0x3E into the edi register prior to the call. We can patch that using the memory window in windbg.
Revisiting the ASP.NET page, things now appear to be working. However I don’t have SQL server on my home machine so I get an exception later in the run when the server is running callback code, though at least we get to that point.
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s