Cecil is slang for cocaine 7
Everybody’s round tripping, ‘tripping assemblies
Some very exciting progress have been made today. Let me explain in what I’ve spend the lasts minutes:
- I have compiled Cecil HEAD normally, this produces a Mono.Cecil.dll file.
- I have compiled this little piece of code referencing the previous assembly:
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> Mono.Cecil;
<span class="kwrd">class</span> Test {
<span class="kwrd">static</span> <span class="kwrd">void</span> Main ()
{
RoundTripCecil ();
}
<span class="kwrd">static</span> <span class="kwrd">void</span> RoundTripCecil ()
{
Console.WriteLine (<span class="str">"Round Trip Cecil"</span>);
IAssemblyDefinition asm = AssemblyFactory.GetAssembly (
<span class="str">"Mono.Cecil.dll"</span>);
Console.WriteLine (<span class="str">"Cecil is loaded"</span>);
AssemblyFactory.SaveAssembly (asm, <span class="str">"Mono.Cecil.dll"</span>, AssemblyKind.Dll);
Console.WriteLine (<span class="str">"Cecil is written back"</span>);
}
}
- I’ve runned it, the result is a fresh new round tripped Mono.Cecil.dll
- I’ve modified the previous code to round trip mcs, our C# compiler (if you still follow me, we are here using a round tripped Cecil to round trip mcs)
- I’ve re-compiled Cecil using the fresh new mcs
- And the new Cecil is perfectly working, wow.
The conclusion of all of that is that the writing layer of Cecil is more and more mature and stable. Few things are missing, especially multiple modules assemblies, but who uses that, and complex custom attributes, using arrays, or properties and fields.
Also the new API is in the SVN, it is now much easier for users to modify and create assemblies. Previous code samples on this blog are now deprecated, maybe I should remove them. Or update them. I will see. If you are already using Cecil, you fool, you may encounter one or two little API break with SVN head. The first that comes to my mind is the method of the AssemblyFactory to get an assembly, wich used to take a string and an LoadingType enumeration. This enumeration does not exists anymore, as I decided to get rid of two different engines to load assemblies. The LazyLoading was introducing a lot of complexity, so for the sake of simplicity, I’ve removed it.
Few things are still not implemented in the modifying layer, but I prefer focusing on the writing one before the end of Google’s Summer of Code. Some times after the end of the SoC, I will release the first public version of Cecil. Wich means API freeze, bugs reports, blah.
Sadly, Cecil itself can’t be use to make some nice screen shot. I had to find other ways:
Assembly Round Tripping and much more
Thanks to another few hacks today on Cecil, I can now do funny things. Assembly Round Tripping is one of them. It just mean that I can now load an Assembly with Cecil, and write it back. Without loss of data. Anyway Cecil for the moment does not deal with a lot of data :) Here is a sample code so that you can figure out by yourself:
<span class="kwrd">using</span> Mono.Cecil;
<span class="kwrd">class</span> RoundTrip {
<span class="kwrd">static</span> <span class="kwrd">void</span> Main ()
{
<span class="kwrd">string</span> file = <span class="str">"hello.exe"</span>
<span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i < 10; i++)
AssemblyFactory.SaveAssembly (AssemblyFactory.GetAssembly (
file, LoadingType.Aggressive), file, AssemblyKind.Console);
}
}
Doing this ten times it plain useless. But now you are sure that the assembly is well round tripped.
Interesting huh ? But yeah, a bit useless as it is. If Cecil is able to read and assembly and write it back, it should also be possible to modify it. To go further, I have to assume that you’ve read the precedent post, that reveals the Hello World example. Now let’s read this wonderful piece of code that will modify the Hello World :
<span class="kwrd">using</span> Mono.Cecil;
<span class="kwrd">using</span> Mono.Cecil.Cil;
<span class="kwrd">class</span> Pre_AOP_Era {
<span class="kwrd">static</span> <span class="kwrd">void</span> Main ()
{
<span class="kwrd">string</span> file = <span class="str">"hello.exe"</span>
IAssemblyDefinition asm = AssemblyFactory.GetAssembly (
file, LoadingType.Aggressive);
ITypeDefinition hello = asm.MainModule.Types [<span class="str">"Test.Hello"</span>];
IMethodDefinition main = hello.Methods [0]; <span class="rem">// get the Main method</span>
ICilWorker worker = main.Body.GetWorker ();
InsertBeforeRetInstruction (
worker, worker.Create (OpCodes.Ldstr, <span class="str">"And from Jb too!"</span>));
InsertBeforeRetInstruction (
worker, worker.Create (OpCodes.Call, <span class="kwrd">typeof</span> (Console).GetMethod (
<span class="str">"WriteLine"</span>, <span class="kwrd">new</span> Type [] {<span class="kwrd">typeof</span> (<span class="kwrd">string</span>)})));
AssemblyFactory.SaveAssembly (asm, file, AssemblyKind.Console);
}
<span class="kwrd">static</span> <span class="kwrd">void</span> InsertBeforeRetInstruction (ICilWorker worker, IInstruction ins)
{
IMethodBody body = worker.GetBody ();
IInstruction ret = body.Instructions [body.Instructions.Count - 1];
worker.InsertBefore (ret, ins);
}
}
Basically, I retrieve here the type and a method defined in the precedent example, and using I add two instructions before the last instruction of this method. I just call Console.WriteLine with a new string “And from Jb too!” The ICilWorker interface defines methods to create and manipulate CIL streams. As it is not very complete, I’ve written here a little method to help in this task.
Unsurprisingly, by invoking the new executable, you’ll be greeted twice:
newton:~/Sources/temp jbevain$ mono hello.exe Hello from Cecil! And from Jb too!
This feature of Cecil is a terribly exciting one. It leds to many applications, including the re-write of the core of AspectDNG, the Aspect Weaver Thomas Gil and I are working on.
Don’t be desapointed, but once again, this is still not in SVN, but it shows the lasts progress of Cecil. I hope you enjoy it as much as I do :)
Oh, and because he loves pictures, here is one of Sebastien Ros, one of my mentor, and me:
The Thousand Nights and a Night of Hacking 1
Hey world, it’s 6am here in Paris, and I’m still in the front of my computer. But this morning, I feel just like happy, excited, and proud. Yeah, and tired too.
I worked all night, and this morning, Cecil have written its first Hello World!. The code is not yet is SVN, it’s still full of debug code, but heh, its works here :)
Here is the little application that emits this new and shiny hello world:
<span class="kwrd">using</span> Mono.Cecil;
<span class="kwrd">using</span> Mono.Cecil.Cil;
<span class="kwrd">class</span> Test {
<span class="kwrd">static</span> <span class="kwrd">void</span> Main ()
{
IAssemblyDefinition asm = AssemblyFactory.DefineAssembly (
<span class="str">"hello"</span>, <span class="str">"HelloWorld"</span>, TargetRuntime.NET_1_1);
ITypeDefinition t = asm.MainModule.DefineType (
<span class="str">"Hello"</span>, <span class="str">"Test"</span>, TypeAttributes.Class, <span class="kwrd">typeof</span> (<span class="kwrd">object</span>));
IMethodDefinition meth = t.DefineMethod (
<span class="str">"Main"</span>, MethodAttributes.Static, <span class="kwrd">typeof</span> (<span class="kwrd">void</span>));
IMethodBody body = meth.DefineBody ();
ICilWorker cw = body.GetWorker ();
cw.Emit (OpCodes.Ldstr, <span class="str">"Hello from Cecil!"</span>);
cw.Emit (OpCodes.Call, <span class="kwrd">typeof</span> (Console).GetMethod (
<span class="str">"WriteLine"</span>, <span class="kwrd">new</span> Type [] {<span class="kwrd">typeof</span> (<span class="kwrd">string</span>)}));
cw.Emit (OpCodes.Ret);
IMethodDefinition ctor = t.DefineConstructor ();
body = ctor.DefineBody ();
cw = body.GetWorker ();
cw.Emit (OpCodes.Ldarg_0);
cw.Emit (OpCodes.Call,
<span class="kwrd">typeof</span> (<span class="kwrd">object</span>).GetConstructor (<span class="kwrd">new</span> Type [0]));
cw.Emit (OpCodes.Ret);
asm.EntryPoint = meth;
AssemblyFactory.SaveAssembly (
asm, <span class="str">"hello.exe"</span>, AssemblyKind.Console);
}
}
The application is simple, and will looks familiar to everyone that already used System.Reflection.Emit. Of course there are some differences, but let see that later, just notice that Cecil understands both Cecil types, and classics System.Type. Oh, and once again, don’t try this at home, it won’t work. At least, it’s not dangerous. As expected, if you execute this file using Mono, you’ll be greeted:
newton:~/Sources/temp jbevain$ mono hello.exe Hello from Cecil!
Don’t expect Cecil to emit more things for the moment. But it’s already not that bad. Let me show you another interesting feature. Imagine you need to generate some ponctual code, that you plan neither to keep, nor to save on the disk. The use case for the AssemblyBuilderAccess.Run enum value within System.Reflection.Emit. Let see how to do that!
Add this import:
<span class="kwrd">using</span> SR = System.Reflection;
And remove the last line of the code above, and replace it the following lines:
SR.Assembly a = AssemblyFactory.CreateReflectionAssembly (
asm, AssemblyKind.Console);
a.GetType (<span class="str">"Test.Hello"</span>).GetMethod (
<span class="str">"Main"</span>, SR.BindingFlags.NonPublic |
SR.BindingFlags.Static
).Invoke (<span class="kwrd">null</span>, <span class="kwrd">new</span> <span class="kwrd">object</span> [0]);
The assembly will be emitted in memory, and loaded as it in an AppDomain (the current one by default). Then you’ll be able to work on it like any other assembly. Cool huh ?
So, tasks for tomorrow: clean the code, improve it, extend it, then it will go into SVN. As I’m happy, for no reason, here is a picture of where Cecil is tinkered. Please notice in the bottom right a good old xbox, that used to be famous. May it rest in peace.
Hum, if the CLR and MS ildasm loads my fresh new hello world, it can’t be run by lauching it just like another executable. It’s not a valid Win32 executable it says. f**k you billy, I’ll see that tomorrow, I’m going to sleep.
Revision 48210 3
On the radio tonight
Brrffrrr frrrfrr frrfr
Assemblies emitted by Cecil are now understood by the .net CLR.
I REPEAT:
Assemblies emitted by Cecil are now understood by the .net CLR.
Brrfrrr frrrffrr frrrf
So that in a near future you can take a short look back in the past and smile
The very first assembly emitted by Cecil, that is readable by Cecil, Mono, Reflector and the .net CLR, can be found here.
La la la lala, lalala.

