NullReferenceException when trying to resolve ModuleReference members #725

Closed
opened 2021-03-11 07:49:19 -06:00 by AnakinRaW · 6 comments
AnakinRaW commented 2021-03-11 07:49:19 -06:00 (Migrated from github.com)

The testcode i used is copied from https://docs.microsoft.com/en-us/dotnet/framework/app-domains/build-multifile-assembly

Scenario:

I compiled the tutorial from the link above.
This yields the following files: myAssembly.exe Client.netmodule and Stringer.netmodule
I'm trying to resolve as many Reference members into their matching Definitions (e.g. TypeReference --> TypeDefinition)

Error Description:

i'm running the following code:


 var appA = AssemblyDefinition.ReadAssembly("myAssembly.exe");

// Get all types from module 'Client.netmodule'
foreach (var t in modules_A.Skip(1).First().Types)
{
    if (t.Name == "<Module>")
        continue;

    // IL_0013: callvirt System.Void myStringer.Stringer::StringerMethod()
    var callInst = t.Methods.First(m => m.Name.Equals("Main")).Body.Instructions[7];

    if (callInst.Operand is not MethodReference methodReference)
        continue;

    // Type Name: myStringer.Stringer
    // TypeRef Scope: Stringer.netmodule ScopeType:ModuleReference
    var methodTypeRef = methodReference.DeclaringType;

    var methodTypeDef = methodTypeRef.Resolve();
}

The line var methodTypeDef = methodTypeRef.Resolve(); fails with an NRE becuase:

  • methodTypeRef 's module is 'Client.netmodule'
  • The ModuleDefinition of 'Client.netmodule' has no Assembly (null). According to ECMA this is valid.
  • The MetadataResolver implementation is as follows: var modules = type.Module.Assembly.Modules; As you see it assumes, a ModuleDefinition always has an Assembly assigned to it.

Solutions

I see a couple of solutions here.

  1. Check whether the module has an assembly
    a. return null in that case, indicating that it was impossible to find a proper TypeDefinition
    b. Have some heuristic-based logic which tries to find a ModuleDefinition which matches the ModuleReferenc's name. Either lookup the assembly resolver's lookup directories for such a module or try on some cached ModuleDefinitions. Return null when no module was found or no module contained to TypeDefinition of the searched TypeReference. Or return the TypeDefinition if found. I think it should be noted however that this approach might return a wrong TypeDefinition if the wrong module matched.
  2. Same as for 1. But instead of returning null, throw a proper exception.

There are probably more and smarter solutions but I think that's up to you to decide @jbevain

The testcode i used is copied from https://docs.microsoft.com/en-us/dotnet/framework/app-domains/build-multifile-assembly ### Scenario: I compiled the tutorial from the link above. This yields the following files: `myAssembly.exe` `Client.netmodule` and `Stringer.netmodule` I'm trying to resolve as many Reference members into their matching Definitions (e.g. TypeReference --> TypeDefinition) ### Error Description: i'm running the following code: ```cs var appA = AssemblyDefinition.ReadAssembly("myAssembly.exe"); // Get all types from module 'Client.netmodule' foreach (var t in modules_A.Skip(1).First().Types) { if (t.Name == "<Module>") continue; // IL_0013: callvirt System.Void myStringer.Stringer::StringerMethod() var callInst = t.Methods.First(m => m.Name.Equals("Main")).Body.Instructions[7]; if (callInst.Operand is not MethodReference methodReference) continue; // Type Name: myStringer.Stringer // TypeRef Scope: Stringer.netmodule ScopeType:ModuleReference var methodTypeRef = methodReference.DeclaringType; var methodTypeDef = methodTypeRef.Resolve(); } ``` The line `var methodTypeDef = methodTypeRef.Resolve();` fails with an NRE becuase: - `methodTypeRef `'s module is 'Client.netmodule' - The ModuleDefinition of 'Client.netmodule' has no Assembly (`null`). According to ECMA this is valid. - The MetadataResolver implementation is as follows: `var modules = type.Module.Assembly.Modules;` As you see it assumes, a ModuleDefinition always has an Assembly assigned to it. ### Solutions I see a couple of solutions here. 1. Check whether the module has an assembly a. return `null` in that case, indicating that it was impossible to find a proper TypeDefinition b. Have some heuristic-based logic which tries to find a ModuleDefinition which matches the ModuleReferenc's name. Either lookup the assembly resolver's lookup directories for such a module or try on some cached ModuleDefinitions. Return null when no module was found or no module contained to TypeDefinition of the searched TypeReference. Or return the TypeDefinition if found. I think it should be noted however that this approach might return a wrong TypeDefinition if the wrong module matched. 2. Same as for 1. But instead of returning null, throw a proper exception. There are probably more and smarter solutions but I think that's up to you to decide @jbevain
jbevain commented 2021-03-12 11:31:47 -06:00 (Migrated from github.com)

Hey, thanks for filing this and investigating.

Could you zip the binaries and attach them to the issue? Thank you!

Hey, thanks for filing this and investigating. Could you zip the binaries and attach them to the issue? Thank you!
jbevain commented 2021-03-12 18:19:23 -06:00 (Migrated from github.com)

Could you zip the binaries and attach them to the issue? Thank you!

Or better yet, start a PR with a failing test!

> Could you zip the binaries and attach them to the issue? Thank you! Or better yet, start a PR with a failing test!
AnakinRaW commented 2021-03-15 06:33:05 -05:00 (Migrated from github.com)

Failed test added in #730

Failed test added in #730
jbevain commented 2021-03-15 12:22:06 -05:00 (Migrated from github.com)

Thank you, I'll have a look!

(Using netmodules is not a good idea they're just a source of pain on earth)

Thank you, I'll have a look! (Using netmodules is not a good idea they're just a source of pain on earth)
AnakinRaW commented 2021-03-15 16:31:24 -05:00 (Migrated from github.com)

Yeah, i'm aware of it. Easily the much flawed design choice of the ECMA Spec, but I'm developing tools tools which need to handle the full ECMA and that sadly includes multi-module assemblies and shared modules.

Yeah, i'm aware of it. Easily the much flawed design choice of the ECMA Spec, but I'm developing tools tools which need to handle the full ECMA and that sadly includes multi-module assemblies and shared modules.
jbevain commented 2021-04-23 16:46:58 -05:00 (Migrated from github.com)

Fixed by #730

Fixed by #730
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
gh-jbevain/cecil#725
No description provided.