Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Injecting IL byte codes

72 views
Skip to first unread message

Mark_Parker

unread,
Apr 6, 2007, 5:44:00 PM4/6/07
to
From the MSDN topic on DynamicMethod I've been able to create a very simple
dynamic method. Following the sample code for DynamicMethod, my method uses
ILGenerator.Emit to emit the opcodes, and everything is fine.

To try something different, I thought I would try to skip the calls to Emit,
and inject the opcodes directly. To do this I created a byte array that
contained the hex bytes that correspond to the opcodes (from the Partition
III ECMA spec for CLI) emitted in the previous version. I then created a
DynamicILInfo instance that I set with the return value from
GetDynamicILInfo() on my dynamic method. After that, I called SetCode on my
DynamicILInfo instance, passing the byte array and the max stack size.
There's some other code, but this is unchanged from the earlier version that
used GetILGenerator and Emit calls.

The new version throws BadImageFormatException when I call the delegate
that's tied to the dynamic method. One of the trouble-shooting tips advises
me to make sure that the file image is a valid managed assembly.

Is there a way to do what I want to do?


Barry Kelly

unread,
Apr 7, 2007, 9:05:54 AM4/7/07
to
Mark_Parker wrote:

> From the MSDN topic on DynamicMethod I've been able to create a very simple
> dynamic method. Following the sample code for DynamicMethod, my method uses
> ILGenerator.Emit to emit the opcodes, and everything is fine.
>
> To try something different, I thought I would try to skip the calls to Emit,
> and inject the opcodes directly. To do this I created a byte array that
> contained the hex bytes that correspond to the opcodes (from the Partition
> III ECMA spec for CLI) emitted in the previous version. I then created a
> DynamicILInfo instance that I set with the return value from
> GetDynamicILInfo() on my dynamic method. After that, I called SetCode on my
> DynamicILInfo instance, passing the byte array and the max stack size.
> There's some other code, but this is unchanged from the earlier version that
> used GetILGenerator and Emit calls.

Here's sample code along those lines:

---8<---
using System;
using System.Reflection.Emit;
using System.Reflection;
using System.IO;

class Program
{
delegate void Method();

static void Main()
{
DynamicMethod dynMeth = new DynamicMethod("foo",
typeof(void), new Type[0], typeof(Program));

// the "easy" way
//WriteHello(dynMeth.GetILGenerator());

// the "hard" way
WriteHelloViaBytes(dynMeth.GetDynamicILInfo());

Method meth = (Method) dynMeth.CreateDelegate(typeof(Method));
meth();
}

static void WriteHelloViaBytes(DynamicILInfo info)
{
MethodInfo writeLineMethod =
typeof(System.Console).GetMethod("WriteLine",
new Type[] { typeof(string) });
int writeLineToken = info.GetTokenFor(
writeLineMethod.MethodHandle);
int helloWorldToken = info.GetTokenFor("Hello World!");

MemoryStream codeStream = new MemoryStream();
BinaryWriter codeWriter = new BinaryWriter(codeStream);

codeWriter.Write((byte) OpCodes.Ldstr.Value);
codeWriter.Write(helloWorldToken);

codeWriter.Write((byte) OpCodes.Call.Value);
codeWriter.Write(writeLineToken);

codeWriter.Write((byte) OpCodes.Ret.Value);

codeWriter.Flush();

info.SetCode(codeStream.ToArray(), 1);

MemoryStream sigStream = new MemoryStream();
BinaryWriter sigWriter = new BinaryWriter(sigStream);

// Signature format is tricky, described in 23.3 of
// ECMA 335; see also CorHdr.h of .NET SDK.
// Takes some time to work out; it's not well
// described, IMHO.

// IMAGE_CEE_CS_CALLCONV_LOCAL_SIG from CorHdr.h
sigWriter.Write((byte) 0x7);
// no of parameters (signatures follow return type, recursively)
sigWriter.Write((byte) 0);
// return type; here I use ELEMENT_TYPE_VOID as in CorHdr.h
sigWriter.Write((byte) 1);

sigWriter.Flush();

info.SetLocalSignature(sigStream.ToArray());
}

static void WriteHello(ILGenerator cg)
{
cg.Emit(OpCodes.Ldstr, "Hello World!");
cg.Emit(OpCodes.Call,
typeof(System.Console).GetMethod("WriteLine",
new Type[] { typeof(string) }));
cg.Emit(OpCodes.Ret);
}
}
--->8---

Be aware that you need to create tokens for all things imported into the
dynamic method; this is a PIA, as is generating the signature. I don't
think you gain a lot by avoiding ILGenerator, unless you've got an
existing .NET compiler and you want to leverage it for DynamicMethod.

> The new version throws BadImageFormatException when I call the delegate
> that's tied to the dynamic method. One of the trouble-shooting tips advises
> me to make sure that the file image is a valid managed assembly.

The "file image" is certainly true for an assembly loaded by bytes, but
the data provided to DynamicMethod is not an entire assembly. It's 4
things:

1) Tokens created for imports, used in code bytes etc.; not just any
bytes will do, they need to be patched up for imports
2) Signature bytes of the method
3) Code bytes
4) Exception bytes

... and they need to be all poked in individually via DynamicILInfo,
like above.

-- Barry

--
http://barrkel.blogspot.com/

Barry Kelly

unread,
Apr 7, 2007, 9:22:07 AM4/7/07
to
Barry Kelly wrote:

> // Signature format is tricky, described in 23.3 of
> // ECMA 335; see also CorHdr.h of .NET SDK.
> // Takes some time to work out; it's not well
> // described, IMHO.
>
> // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG from CorHdr.h
> sigWriter.Write((byte) 0x7);
> // no of parameters (signatures follow return type, recursively)
> sigWriter.Write((byte) 0);
> // return type; here I use ELEMENT_TYPE_VOID as in CorHdr.h
> sigWriter.Write((byte) 1);

And it's a demonstration proof of its trickiness: the above is the
format for a method signature, not local variables signature. Doh!

This is described in section 23.2.6, part II, ECMA 335.

IMAGE_CEE_CS_CALLCONV_LOCAL_SIG is 0x7, and is the first part of the
locals signature. Second byte is count (number of locals); and that's
it, the signatures for the locals follow. The code quoted above should
read thusly:

---8<---


// Signature format is tricky, described in 23.3 of
// ECMA 335; see also CorHdr.h of .NET SDK.
// Takes some time to work out; it's not well
// described, IMHO.

// IMAGE_CEE_CS_CALLCONV_LOCAL_SIG from CorHdr.h
sigWriter.Write((byte) 0x7);

// no of locals (signatures follow, recursively)
sigWriter.Write((byte) 0);
--->8---

It doesn't make practical runtime difference, however; the extra '0'
byte in the parent post is ignored.

Mark_Parker

unread,
Apr 9, 2007, 2:44:02 PM4/9/07
to
Thanks, Barry. It was indeed the signature that I was missing. Your example
got me started and section 23 of ECMA 335 gave me the information I needed to
insert the right bytes into the signature.

So in a nutshell, what I have is one stream of IL code bytes for the method,
and another stream of attribute bytes representing, respectively, the
calling convention (IMAGE_CEE_CS_CALL_CONV_LOCAL_SIG), the number of
parameters (2 in the case of my method), the return type (long), and a byte
each for the types of the parameters (int for both).

Mark

Smartkid

unread,
May 26, 2007, 3:26:21 AM5/26/07
to
I think that the RAIL(Runtime Assembly Instrumentation Library) may help

http://csharp-source.net/open-source/aspect-oriented-frameworks/runtime-assembly-instrumentation-library


My be a litter later :-)


"Mark_Parker" <Mark_...@discussions.microsoft.com> wrote in message
news:60AA5108-F566-4E12...@microsoft.com...

0 new messages