Aug 15, 2015

Invalid offsets in IL instructions after modifying assembly with Mono.Cecil

Hi

Since 2008 I've been using Mono.Cecil, an amazing piece of software, that exposes .Net assemblies contents in a relatively easy and intuitive way so one can inspect and/or change the assembly's contents. 

Unfortunately, in order to use Mono.Cecil effectively, you need a fair amount of knowledge about how MS IL works and Mono.Cecil documentation is kind of sparse (to say the least).

Some time ago I was using it to change some assembly IL and to my surprise after applying the changes peverify complained that some IL instructions had invalid offsets

After some head scratching I've figured out that the issue was that the target of a (short) branch instruction have crossed the threshold that would require it to be  a normal branch (i.e, one that could use 32 bits offsets instead of 8 bits of the short version).

So now the issue was that I'd be forced to scan every single IL instruction in the method body and check/fix the target offset of branches; fortunately, Mono.Cecil has  two methods: MethodBody.SimplifyMacros() and  MethodBody.OptimizeMacros() that can be used to achieve my goal. 

Basically before start doing changes to the method body's IL, you call SimplifyMacros() and when you've finished with your changes on that method body you call OptimizeMacros() and Cecil will take care of adjusting branches accordingly. Nice!


Thanks everybody that helped to develop Mono.Cecil! It's really a handy library! :)

(Leia este post em português)

Instruções com offsets IL inválidos após modificar assembly com Mono.Cecil

Desde 2008 venho usando a biblioteca (muito boa diga-se de passagem) Mono.Cecil que permite você tanto ler quanto modificar o conteúdo de assemblies .Net de uma forma relativamente simples (depois que você compreende como utilizá-la, pois infelizmente,  usar tal biblioteca de forma efetiva exige um bom conhecimento sobre como o MS IL funciona e a documentação do Mono.Cecil deixa a desejar).

Algum tempo atras, um dos testes de um dos meus aplicativos (que usa esta biblioteca) começou a falhar; para ser mais preciso peverify começou a reportar algumas instruções com offsets inválidos

Após investigar por algum tempo concluí que o problema se encontrava no offset (operando) usado em alguma instruções de desvio (branch), as quais estavam ultrapassando o limite de um byte (-127 / 128) (isto ocorria devido a dois motivos: i) o código IL usava a forma (short) branch, ou seja, a instrução de desvio utilizada aceitava um único byte como offset e ii) meu aplicativo adicionava instruções entre a instrução de desvio e o alvo do desvio efetivamente exigindo um offset maior que 128).

Munido desta nova informação, tudo que tinha que fazer agora era verificar instrução por instrução (é claro, apenas as de desvio) se o operando da mesma estava dentro da faixa válida (-127 a 128) e corrigir quaisquer uma que não esteja. É claro que eu queria evitar isto a qualquer custo, pois este processo adicionaria mais código (e possíveis bugs) a meu aplicativo. 

Felizmente, Mono.Cecil possui dois método (MethodBody.SimplifyMacros() e  MethodBody.OptimizeMacros()) que, quando usados, se encubem em garantir que os operandos (offset) das instruções de desvio estão dentro das faixas válidas e caso não estejam, trocam a instrução para uma instrução de desvio que suporte o offset em questão (neste  case uma que use 4 bytes).

Basicamente, antes de iniciar qualquer modificação nas instruções (IL) de um método, você executa o método SimplifyMacros() e quando finalizar suas modificações você executa OptimizeMacros() e Mono.Cecil se incumbirá em ajustar as instruções de desvio (se necessário).


Desenvolvedores do Mono.Cecil: meu muito obrigado! :)

Have fun

(Read this post in english)

Aug 7, 2015

Resolução de métodos sobrecarregados (overloaded) em C#

Depois de um longo e tenebroso tempo em silêncio, eu voltei :)

Desta vez com um pequeno teste.

Dado o seguinte programa em C# (não importa muito a versão da linguagem), oque você espera ver no console?

using static System.Console;

class Foo 
{ 
 public void Bar(string[] o) { WriteLine("string[]"); } 
 public void Bar(object o) { WriteLine("object"); } 

 static void Main()
 {
  var foo = new Foo();
  foo.Bar(null); 
 } 
}

Responda através dos comentários

Boa diversão!

Read this post in English!

Overload resolution in C#

After a long dark period of silence I am back :)

This time a little quiz.

Given the following C# program, what you expect to get printed?

using static System.Console;

class Foo 
{ 
 public void Bar(string[] o) { WriteLine("string[]"); } 
 public void Bar(object o) { WriteLine("object"); } 

 static void Main()
 {
  var foo = new Foo();
  foo.Bar(null); 
 } 
}

Answers (if any), in comments ;)

Have fun!

Leia este post em Português!