[mono-cecil] using OpCodes.Ldloca_S

400 views
Skip to first unread message

Louis Salin

unread,
May 7, 2010, 8:38:04 PM5/7/10
to mono-cecil
Hey guys,

I'm currently trying to inject a new class into an assembly. I've been
creating the same instructions that I see when using a disassembler
and I'm stuck on this opCode:

Ldloca_S V_0

I'm not quite sure how to use the CilWorker to insert that instruction
into my method body. The MS doc seems to indicate that it takes a byte
as a parameter, which makes sense, but I get an argument exception
when doing this:

init.Body.CilWorker.Append(init.Body.CilWorker.Create(OpCodes.Ldloca_S,
0));

Here's the code I'm trying to put in:

someVar = string.Format("start time:{0}\n",
DateTime.Now.Ticks.ToString());

which is decompiled into the following IL:

IL_0011: ldstr "start time:{0}"
IL_0016: call System.DateTime.get_Now()
IL_001B: stloc.0
IL_001C: ldloca.s V_0
IL_001E: call System.DateTime.get_Ticks()
IL_0023: stloc.1
IL_0024: ldloca.s V_1
IL_0026: call System.Int64.ToString()
IL_002B: call System.String.Format(System.String,System.Object)

How can I append the Ldloca_S instruction?

Thanks!

Louis

--
--
mono-cecil

Jb Evain

unread,
May 7, 2010, 8:45:48 PM5/7/10
to mono-cecil
On May 8, 2:38 am, Louis Salin <louis.p...@gmail.com> wrote:
> How can I append the Ldloca_S instruction?

Give it the VariableDefinition you want to load the address of.

Also it's strongly recommended you use 0.9.2 instead of a 0.6 version.

--
--
mono-cecil

Louis Salin

unread,
May 8, 2010, 12:16:18 AM5/8/10
to mono-cecil
Hey JB,

Thanks for the quick reply.
I've upgraded my code to 0.9.2. I'm not sure how to use a
VariableDefinition to hold the values of get_Now and get_Ticks in the
code I've posted earlier. Maybe I shouldn't try to write what the
disassembler is giving me...

Here's what I have so far (obviously, the Ldlocal_S instructions
fail):

var processor = init.Body.GetILProcessor();
processor.Append(processor.Create(OpCodes.Ldstr, "start time:{0}\n"));
met =
assembly.MainModule.Import(typeof(DateTime).GetMethod("get_Now"));
processor.Append(processor.Create(OpCodes.Call, met));
processor.Append(processor.Create(OpCodes.Stloc_0));

processor.Append(processor.Create(OpCodes.Ldloca_S, 0));

met =
assembly.MainModule.Import(typeof(DateTime).GetMethod("get_Ticks"));
processor.Append(processor.Create(OpCodes.Call, met));
processor.Append(processor.Create(OpCodes.Stloc_1));

processor.Append(processor.Create(OpCodes.Ldloca_S, 1));

met = assembly.MainModule.Import(typeof(int).GetMethod("ToString"));
processor.Append(processor.Create(OpCodes.Call, met));

met = assembly.MainModule.Import(typeof(string).GetMethod("Format",
new [] {typeof(string), typeof(object)}));
processor.Append(processor.Create(OpCodes.Call, met));


Thanks for all you help!

Louis
--
--
mono-cecil

Jb Evain

unread,
May 8, 2010, 12:48:07 AM5/8/10
to mono-...@googlegroups.com
Hey,

On Sat, May 8, 2010 at 6:16 AM, Louis Salin <louis...@gmail.com> wrote:
> Thanks for the quick reply.
> I've upgraded my code to 0.9.2.

Cool!

> I'm not sure how to use a
> VariableDefinition to hold the values of get_Now and get_Ticks in the
> code I've posted earlier. Maybe I shouldn't try to write what the
> disassembler is giving me...

Actually, it's usually a very good strategy.

In ildasm, you can see that DateTime.get_Now returns a
System.DateTime, and that System.DateTime.get_Ticks returns a long (a
System.Int64).

stloc/ldloc work on variables, so you need to create appropriate variables.

> var processor = init.Body.GetILProcessor();

Here you would add:

var date_now = new VariableDefinition (module.Import (typeof (DateTime)));
var date_ticks = new VariableDefinition (module.Import (typeof (long)));

init.Body.Variables.Add (date_now);
init.Body.Variables.Add (date_ticks);
init.Body.InitLocals = true;

Also note that Append(Create()) can be simplified with Emit.

> processor.Append(processor.Create(OpCodes.Ldstr, "start time:{0}\n"));
> met =
> assembly.MainModule.Import(typeof(DateTime).GetMethod("get_Now"));
> processor.Append(processor.Create(OpCodes.Call, met));
> processor.Append(processor.Create(OpCodes.Stloc_0));

Change to:

processor.Emit (OpCodes.Stloc, date_now);

> processor.Append(processor.Create(OpCodes.Ldloca_S, 0));

processor.Emit (OpCodes.Ldloca_S, date_now);

> met =
> assembly.MainModule.Import(typeof(DateTime).GetMethod("get_Ticks"));
> processor.Append(processor.Create(OpCodes.Call, met));
> processor.Append(processor.Create(OpCodes.Stloc_1));

processor.Emit (OpCodes.Stloc, date_ticks);

> processor.Append(processor.Create(OpCodes.Ldloca_S, 1));

processor.Emit (OpCodes.Ldloca_S, date_ticks);

> met = assembly.MainModule.Import(typeof(int).GetMethod("ToString"));

You want to call typeof (long).GetMethod ("ToString"), not int.ToString.

> processor.Append(processor.Create(OpCodes.Call, met));

You need to use Callvirt for ToString, it's a virtual method

> met = assembly.MainModule.Import(typeof(string).GetMethod("Format",
> new [] {typeof(string), typeof(object)}));
> processor.Append(processor.Create(OpCodes.Call, met));

And you should

--
Jb Evain <j...@nurv.fr>

--
--
mono-cecil

Jb Evain

unread,
May 8, 2010, 12:48:53 AM5/8/10
to mono-...@googlegroups.com
On Sat, May 8, 2010 at 6:48 AM, Jb Evain <j...@nurv.fr> wrote:
> And you should

be ok.

Louis Salin

unread,
May 8, 2010, 8:19:45 AM5/8/10
to mono-cecil
Cool, thank you so much. There are a lot of details to know about!

Why didn't the disassembled IL call the virtual ToString and simply
performs a regular call instead? And why can't I see variables in the
IL for the get_Now and get_Ticks calls? Are V_0 and V_1 in the IL
constants that reference some kind of variable storage inside the
runtime?

Again, thanks a lot for your help! When I'm done with this, I'll try
to help you out and write something for your FAQ!

Louis

On May 7, 11:48 pm, Jb Evain <j...@nurv.fr> wrote:
> Hey,
>
--
--
mono-cecil

Jb Evain

unread,
May 8, 2010, 8:22:38 AM5/8/10
to mono-...@googlegroups.com
Hey,

On Sat, May 8, 2010 at 2:19 PM, Louis Salin <louis...@gmail.com> wrote:
> Why didn't the disassembled IL call the virtual ToString and simply
> performs a regular call instead?

Actually it's safe to use `call` here as Int64 is a value type.

> And why can't I see variables in the
> IL for the get_Now and get_Ticks calls? Are V_0 and V_1 in the IL
> constants that reference some kind of variable storage inside the
> runtime?

Nope, there's nothing special about them. If your code uses stloc_1
and stloc_0, they have to be defined. Look in ildasm at the top of
the method, there's a definition for each variable used in the method
body.

> Again, thanks a lot for your help! When I'm done with this, I'll try
> to help you out and write something for your FAQ!

Cool, thanks!
Reply all
Reply to author
Forward
0 new messages