ExceptionInformation blocks, possible issue?

Mar 8, 2010 at 10:01 PM

Not sure if this is an issue or a design decision. I've been working on some abstractions and noticed this with exception handling. I'm not sure if this is the right place for this but i'll ask anyways.

 

When looking at the ExceptionInformation of a decompiled application  the TryEndOffset points to the HandlerStartAddress. This is a bit confusing. For instance, when a try has multipule catches associated with it multipule ExceptionInformation objects are created. the "TryEndOffset" for each of those handlers points to the same address of the HandlerStartAddress.

 

Example:
try{

 

}catch (Exception1 ex){

 

}catch(Exception2 ex){

 

}

 

This results in two exception handler information blocks. The TryStartOffset is the same in both, the TryEndOffset points to the HandlerStartAddress for each of the handlers and the HandlerEndOffset points to the appropriate location. My question is is this correct? Shouldnt the TryStartOffset - TryEndOffset only encompass the instruction between the Try{ } and be the same for each blocks? If this is a bug I'll fix it and submit a patch. It's been causing me all sorts of issues and a forcing me to create a few work arounds in my own code.

Thanks,

    John

Mar 10, 2010 at 5:36 PM
Edited Mar 10, 2010 at 6:04 PM

Ok, turns out it was my issue. Is there another way to end handler scope without ending the try block? I end up in a situation where there is multiple handler blocks and getting the scopes to end. I call if I call CreateTryBody only 1 time and emit.EndTryBody at the end when try to create the handlers (since the handlers come after the try bodyend) I get an exception. When is EndTryBody supposed to be called? After the last Handler? if that's the case how do I get the handler scopes to end properly? Does another method need to be introduced? Like EndHandlerBody. Any help would be appreciated.

 

*edit

I tried to mimic what i saw in the ILMutator sample and that has this bug present.  The way ILMutator.cs handles the exceptions doesnt work with multiple handlers and messes up the exceptioninfo definitions such that the TryEndOffset -> points to HandlerStart after it rewrites the IL.

 

Mar 10, 2010 at 9:54 PM

Ok so to fix this issue, i did a few things. Essentually i had to maintain a stack myself to know when scope ended. (Just a refernce to the last most handlerOffset for that try catch block. I added two methods a EndHandlerScope() and a PopTryBlock(). I don't think this is the best solution, but it works for now.

    public void EndHandler()
    {
        ILGeneratorLabel handlerEnd = new ILGeneratorLabel(false);
        this.MarkLabel(handlerEnd);
        for (int i = this.handlers.Count - 1; i >= 0; i--)
        {
            if (this.handlers[i].HandlerEnd == null)
            {
                this.handlers[i].HandlerEnd = handlerEnd;
                break;
            }
        }
    }
    /// <summery> 
    /// Pops the last try scope
    /// </summery>
    public void PopTryScope()
    {
        if (this.tryBodyStack.Count > 0)
        {
            this.tryBodyStack.Pop();
        }
    }

 

Along with this code of mine. (Not a full snippet but hopefully you get the gist)

 

if (bop.IsTryStart)
{
      BaseOperation lastHandlerOp = null; 
      foreach (ExceptionInfo inf in bop.ExceptionInfo)
      {
             if (inf.TryStartOp == bop)
             {
                    generator.BeginTryBody();
             }
             if (lastHandlerOp == null && inf.HandlerLastOp != null)
             {
                    lastHandlerOp = inf.HandlerLastOp; 
             }
             int lastOffset = bops.IndexOf(lastHandlerOp);
             if (inf.HandlerLastOp != null && lastOffset < bops.IndexOf(inf.HandlerLastOp))
             {
                    lastHandlerOp = inf.HandlerLastOp;
             }
      }
      if (lastHandlerOp != null)
      {
            lastHandlerOpInTryBlock.Push(lastHandlerOp);
      }
}

then i can test if the bop is a handler end, and end the handler, afterwhich i test if it's the current scopes last handler instruction, in which case i pop the tryblock stack.

this works on complex nested try/catch blocks, reproducing the correct ExceptionInformation blocks as the original. See blow for my "sample" program. If you run this through the ILMutator sample and look at the resulting ExceptionInformation blocks you'll see errors. on the new method definition.

  class Program
    {
        static void Main(string[] args)
        {
            int y = 0;
            if(y == 0){
                y++;
                try
                {
                    if (y == 33)
                    {
                        try
                        {
                            y++;
                        }
                        catch (Exception e)
                        {

                        }
                    }
                    int i = 0;
                    i++; 
                }
                catch (ArgumentException ae)
                {
                    y += 2;
                    Console.WriteLine(ae.Message);
                }
                catch (Exception e)
                {
                    y += 4;
                    Console.WriteLine(e.Message);
                }
            }else{
                y = 45;
                try
                {
                    try
                    {
                        y++;
                    }catch(ArgumentException e){
                        y--;
                    }
                    y++;
                    y++;
                }
                catch (Exception e)
                {
                    y--;
                }
            }
            y--;
            Console.WriteLine(String.Format("The answer is {0}", y)); 
            return;
        }
    }