Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dnlib fails to resolve types according to framework version #20

Closed
yck1509 opened this issue Dec 6, 2014 · 12 comments
Closed

dnlib fails to resolve types according to framework version #20

yck1509 opened this issue Dec 6, 2014 · 12 comments

Comments

@yck1509
Copy link
Contributor

yck1509 commented Dec 6, 2014

For example, suppose I have a library in .NET 3.5. There is a method taking an argument of System.Action in System.Core. Then I created an application in .NET 4 to use that library's method.

In this application, the argument's type is System.Action in mscorlib, since it has moved to mscorlib in .NET 4. The runtime will use .NET 4's System.Core, which has exported System.Action to mscorlib and has no problem. However, dnlib will use .NET 3.5's System.Core and cannot match the reference in the application.

@0xd4d
Copy link
Collaborator

0xd4d commented Dec 8, 2014

What fails, the resolver? Does it work if you force the assembly resolver to only resolve .NET 4.0 assemblies by updating the search paths?

Also, do you have a simple test program?

@yck1509
Copy link
Contributor Author

yck1509 commented Dec 8, 2014

I do have a test program, however it's quite complex:
First, create a library in .NET 3.5:

using System;
public class TestA {
    public static void Test(Action act) {
        act();
    }
}

Second, create an application in .NET 4.0, referencing the first library:

using System;
public class TestB {
    public static void Main() {
        TestA.Test(() => Console.WriteLine("OK"));
    }
}

Then the following code would demonstrate the issue:

using System;
using System.Diagnostics;
using dnlib;
using dnlib.DotNet;

class Program {
    static void Main(string[] args) {
        var module = ModuleDefMD.Load(@"SampleB.exe");
        var type = module.Find("TestB", true);
        var method = type.FindMethod("Main");
        foreach (var instr in method.Body.Instructions) {
            if (instr.Operand is IMethod && ((IMethod)instr.Operand).Name == "Test") {
                var methodRef = (IMethod)instr.Operand;
                var methodDef = methodRef.ResolveMethodDef();
                Debug.Assert(methodDef != null);
            }
        }
    }
}

The assertion failed here. It should be able to resolve to TestA.Test in the first library.
How to force the resolver to only resolve .NET 4 assemblies? Use PreSearchPaths/PostSearchPaths?
Also, Mono.Cecil can resolve it correctly, however they ignore the scope of method reference, marked as a TODO. The framework fusion worked by having a 'framework config' redirect:

LOG: Version redirect found in framework config: 3.5.0.0 redirected to 4.0.0.0.
LOG: Post-policy reference: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

However, I'm unable to find where this 'framework config' is located.

@0xd4d
Copy link
Collaborator

0xd4d commented Dec 8, 2014

How to force the resolver to only resolve .NET 4 assemblies? Use PreSearchPaths/PostSearchPaths?

Yes, and make sure they're empty first so it won't try to keep looking if it doesn't find the exact assembly (correct name + version). FindExactMatch prop should also be false.

@yck1509
Copy link
Contributor Author

yck1509 commented Dec 8, 2014

It works when I use the following module loading code:

        var resolver = new AssemblyResolver();
        resolver.PreSearchPaths.Clear();
        resolver.PreSearchPaths.Add(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\");
        resolver.FindExactMatch = false;
        var moduleCtx = ModuleDefMD.CreateModuleContext(false);
        moduleCtx.AssemblyResolver = resolver;

        var module = ModuleDefMD.Load(@"SampleB.exe", moduleCtx);

@0xd4d
Copy link
Collaborator

0xd4d commented Dec 8, 2014

Cool, I might add some code (simple methods to call) to make that easier so you don't have to add the paths yourself like above. FindExactMatch should be false by default though, but I mentioned it above just in case you had set it to true.

@yck1509
Copy link
Contributor Author

yck1509 commented Dec 8, 2014

So does it mean there should be 2 different assembly resolvers, one for .NET 3.5, one for .NET 4, for dnlib to resolve the references correctly?

@0xd4d
Copy link
Collaborator

0xd4d commented Dec 8, 2014

No, but if you have 2+ assemblies and you know that the main exe will use .NET 4.0, then you might need to make sure only .NET 4.0 search paths are used if the other one uses another earlier .NET version, as in your example above. This would match the runtime's behaviour: .NET 4.0 runtime only searches for .NET 4.0 DLLs. By default, dnlib will try to use the exact DLL file because I think this is what most people would want and expect.

@yck1509
Copy link
Contributor Author

yck1509 commented Dec 8, 2014

Okay, thanks for your help!

@0xd4d 0xd4d closed this as completed Dec 10, 2014
@yck1509
Copy link
Contributor Author

yck1509 commented Dec 11, 2014

I've finally find the information for framework version redirect by reversing the runtime. You can find my fix in bfc3340. I also fix the MemberRef resolving problem in quite complicated way in 4000f37. It might be useful.

@0xd4d
Copy link
Collaborator

0xd4d commented Dec 11, 2014

Looks good! I'll add those to my dnlib too probably tomorrow. I noticed that you used default arguments, and I'll add a commmit that undoes that by adding extension methods or extra methods because VS2008 doesn't support default arguments (AFAICR).

BTW, are those redirects hardcoded or do they exist in some XML file?

@yck1509
Copy link
Contributor Author

yck1509 commented Dec 12, 2014

These redirects are hardcoded into clr.dll/mscorwks.dll, I extract them using the following code:

        static unsafe void frRedirV2() {
            // .NET 3.5, x86
            var clrBase = (byte*)Process.GetCurrentProcess().Modules.OfType<ProcessModule>().Single(m => m.ModuleName == "mscorwks.dll").BaseAddress;
            IntPtr* fXPolicy = (IntPtr*)(clrBase + 0xB98A0);                  // g_arFxPolicy

            var frRedirs = new List<Tuple<string, string, string>>();

            while (((uint)fXPolicy[0] & 0xff000000) == ((uint)fXPolicy[1] & 0xff000000)) {
                var frName = Marshal.PtrToStringUni(fXPolicy[0]);
                var frKey = Marshal.PtrToStringUni(fXPolicy[1]);
                var frVer = Marshal.PtrToStringUni(fXPolicy[2]);
                frRedirs.Add(Tuple.Create(frName, frKey, frVer));
                fXPolicy += 3;
            }
            var list = string.Join(Environment.NewLine, frRedirs.Select(redir => redir.ToString()).ToArray());
        }

        static unsafe void frRedirV4() {
            // .NET 4.5, x86
            var clrBase =
                (byte*)Process.GetCurrentProcess().Modules.OfType<ProcessModule>().Single(m => m.ModuleName == "clr.dll").BaseAddress;
            byte* fXPolicy = clrBase + 0x54380;                  // g_arFxPolicy
            IntPtr* partStrs = (IntPtr*)(clrBase + 0x54850);     // g_rgAssemblyNamePartStrings
            IntPtr* kVerStrs = (IntPtr*)(clrBase + 0x553D4);     // g_rgAssemblyKeyVersionStrings

            var frRedirs = new List<Tuple<string, string, string, bool>>();

            while (fXPolicy[5] != 0) {
                var frName = new StringBuilder();
                for (int i = 0; i < 4; i++) {
                    if (fXPolicy[i] != 0)
                        frName.Append(Marshal.PtrToStringAnsi(partStrs[fXPolicy[i]]) + ".");
                    else
                        break;
                }
                var frKey = Marshal.PtrToStringUni(kVerStrs[fXPolicy[4]]);
                var frVer = Marshal.PtrToStringUni(kVerStrs[fXPolicy[5] & 0x7f]);
                var flag = (fXPolicy[5] >> 7) != 0;
                frRedirs.Add(Tuple.Create(frName.ToString().Trim('.'), frKey, frVer, flag));
                fXPolicy += 6;
            }
            var list = string.Join(Environment.NewLine, frRedirs);
        }

Your addresses may vary.

@0xd4d
Copy link
Collaborator

0xd4d commented Dec 12, 2014

I've now added your two commits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant