Hi Mort,
>
> Does anyone know what sort of algorithm is used to find matching push
> and pop instructions. I assume that an instruction that pops something
> off the stack can have many instructions that could have pushed it
> when a loop is involved.
>
I have been using ControlFlowGraph from Cecil.Decompiler to do
something similar in a project of mine. You are correct that there are
potentially different possible execution paths that could yield the
stack item(s) at a particular instruction. Take this simple example:
class Test {
static void Main()
{
string a = null;
string b = "foo";
string c = a ?? b;
}
}
The IL generated by gmcs for Main() is:
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: ldstr "foo"
IL_0007: stloc.1
IL_0008: ldloc.0
IL_0009: dup
IL_000a: brtrue IL_0011
IL_000f: pop
IL_0010: ldloc.1
IL_0011: stloc.2
IL_0012: ret
Note that either IL_0008 or IL_0010 could be responsible for the top
stack item at IL_0011. If you would like to use Cecil.Decompiler, here
are a couple extension methods I cooked up for my project that might
help you too:
using Mono.Cecil.Cil;
using Cecil.Decompiler;
using System.Linq;
// Finds the first instruction(s) responsible for the item at the top
of the stack as visible from the specified instruction.
public static IEnumerable<Instruction> FindLastStackItem (this
ControlFlowGraph cfg, InstructionBlock block, Instruction current)
{
return cfg.FindStackHeight (block, current, cfg.GetData
(current).StackBefore - 1);
}
// Finds the last instruction(s) at the specified stack height as
visible from the specified instruction
public static IEnumerable<Instruction> FindStackHeight (this
ControlFlowGraph cfg, InstructionBlock block, Instruction current, int
stackHeight)
{
while (cfg.GetData (current).StackBefore > stackHeight) {
if (current == block.First) {
return cfg.GetPredecessors (block).Select (b =>
cfg.FindStackHeight (b, b.Last, stackHeight)).Flatten ().Distinct ();
} else
current = current.Previous;
}
if (current.OpCode == OpCodes.Dup)
return cfg.FindLastStackItem (block, current);
return new Instruction [] { current };
}
// utility:
public static IEnumerable<InstructionBlock> GetPredecessors (this
ControlFlowGraph cfg, InstructionBlock ib)
{
return cfg.Blocks.Where (b => b.Successors.Contains (ib));
}
public static IEnumerable<T> Flatten<T> (this
IEnumerable<IEnumerable<T>> enumerable)
{
foreach (var subenumerable in enumerable)
foreach (var item in subenumerable)
yield return item;
}
These methods have worked for me so far, but I am not an expert in
static analysis by any means. Hope this is helpful!
-Alex
--
--
mono-cecil