2017-03-12 43 views
6

此問題是關於自定義C#IL代碼的靜態堆棧分析以及如何設計操作碼以滿足編譯器。C#IL代碼修改 - 保持堆棧完好

我有代碼修改現有的C#方法通過附加我自己的代碼。爲了避免原始方法在執行代碼之前返回,它會將所有RET操作碼替換爲BR endlabel,並將該標籤添加到原始代碼的末尾。然後我在那裏添加更多的代碼,最後添加一個RET。

這一切工作正常,但在某些方法上失敗。下面是一個簡單的例子:

public static string SomeMethod(int val) 
{ 
    switch (val) 
    { 
     case 0: 
      return "string1".convert(); 
     case 1: 
      return "string2".convert(); 
     case 2: 
      return "string3".convert(); 
     // ... 
    } 
    return ""; 
} 

這是由該IL代碼表示:

.method public hidebysig static string SomeMethod(int32 val) cil managed 
{ 
    .maxstack 1 
    .locals val ([0] int32 num) 
    L_0000: ldarg.0 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: switch (L_002e, L_004f, L_0044, ...) 
    L_002c: br.s L_0091 
    L_002e: ldstr "string1" 
    L_0033: call string Foo::convert(string) 
    L_0038: ret 
    L_0039: ldstr "string2" 
    L_003e: call string Foo::convert(string) 
    L_0043: ret 
    L_0044: ldstr "string3" 
    L_0049: call string Foo::convert(string) 
    L_004e: ret 
    ... 
    L_0091: ldstr "" 
    L_0096: ret 
} 

我的程序修改了它之後,代碼如下:

.method public hidebysig static string SomeMethod(int32 val) cil managed 
{ 
    .maxstack 1 
    .locals val ([0] int32 num) 
    L_0000: ldarg.0 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: switch (L_002e, L_004f, L_0044, ...) 
    L_002c: br.s L_0091 
    L_002e: ldstr "string1" 
    L_0033: call string Foo::convert(string) 
    L_0038: br L_009b // was ret 
    L_0039: ldstr "string2" 
    L_003e: call string Foo::convert(string) 
    L_0043: br L_009b // was ret 
    L_0044: ldstr "string3" 
    L_0049: call string Foo::convert(string) 
    L_004e: br L_009b // was ret 
    ... 
    L_0091: ldstr "" 
    L_0096: br L_009b // was ret 
    L_009b: my code here 
    ... 
    L_0200: ret 
} 

和我編譯錯誤:

Could not execute post-long-event action. Exception: System.TypeInitializationException: An exception was thrown by the type initializer for FooBar ---> System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) Foo:SomeMethod (int): IL_0000: ldnull

是否有任何簡單的方法以通用方式替換RET並保持靜態分析器的快樂?

+6

解決了這個問題。用BR代替RET會增加代碼長度,短跳可能會變得非法。解決方案是用長跳躍替換它們。經過測試和工作。 –

+1

你也可以使用try-finally子句;這將避免你所有的麻煩。當然,如果你想總是執行那些代碼,它也會執行一個異常。根據你注入的代碼,這可能是一件好事或一件壞事。 – Luaan

+0

對你自己的錯誤有很好的理解:)你應該自己回答 –

回答

3

原來,這個問題是所有的短跳轉指令可能可能變得太遙遠,因爲插入BR,而不是RET增加操作碼大小。

我解決了這個問題,把所有以「_S」結尾的操作碼都替換成相應的跳轉版本。有關這方面的更多詳細信息,請看看這個提交到我的項目:Fixed illegal short jumps

+2

@IlianPinzon,我正在等待我在24小時之前的冷靜期,在stackoverflow允許我接受我的_own_答案。 –