Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating a Portable Class Library through Reflection.Emit

I am writing a compiler which generates on-disk .NET assemblies using the System.Reflection.Emit API. The compiler itself is built against .NET 4.5, but the generated code only references types from Portable Class Libraries. However, when trying to reference a generated assembly from a Windows Phone 8 project, Visual Studio complains that A reference to a higher version or incompatible assembly cannot be added to the project.

When opening the generated assembly in a decompiler, I can see that it references two PCLs plus mscorlib 4.0.0.0, whereas I understand that PCLs are supposed to reference mscorlib 2.0.5.0.

Is there a way to make make the System.Reflection.Emit API generate PCLs, or is my only option to migrate to Mono.Cecil?

like image 433
Trillian Avatar asked Feb 20 '14 00:02

Trillian


1 Answers

All right, I'll answer my own question.

I've found no evidence that the System.Reflection.Emit APIs can generate assemblies referencing another version of mscorlib than the one used used by the current process. Indeed, the APIs that take System.Type parameters and other reflection objects presumably add a reference to the result of querying their Type.Assembly property, which corresponds to the version of mscorlib in use.

However, portable class libraries aren't that different from what System.Reflection.Emit generates, so it is possible to patch the assemblies after the fact to "make them portable". Disclaimer: this requires familiarity with the PE file format and might have unforeseen side-effects, but it works for me:

  • When generating the assembly, use AssemblyBuilder.SetCustomAttribute to add this attribute to the assembly:

    [System.Runtime.Versioning.TargetFrameworkAttribute(".NETPortable,Version=v4.0,Profile=Profile136", FrameworkDisplayName = ".NET Portable Subset")]
    
  • This is where it gets sketchy: after having called AssemblyBuilder.Save, open a read/write file stream to the generated assembly, walk through the PE, COFF, COM, CLI and metadata table headers to locate the AssemblyRef table row for mscorlib. Modify the referenced version of mscorlib to 2.0.5.0, add 0x100 to its flags ("retargetable") and update its public key token blob to 0x7CEC85D7BEA7798E.

Note that if you use other framework assemblies, you might need to patch their references as well (I haven't tested this). Otherwise, voilà! The assembly is now portable and can be used, for example, in a Windows Phone project.

(... or just use Mono.Cecil/IKVM.Reflection ...)

Edit: The code I used can be found on github. This is a huge hack so the usual disclaimers apply, use at your own risks.

like image 84
Trillian Avatar answered Nov 20 '22 05:11

Trillian