Linguagem Intermediária Comum

Common Intermediate Language ( CIL ), anteriormente chamado Microsoft Intermediate Language ( MSIL ) ou Intermediate Language ( IL ), [1] é o conjunto de instruções binárias de linguagem intermediária definido na especificação Common Language Infrastructure (CLI). [2] As instruções CIL são executadas por um ambiente de tempo de execução compatível com CLI, como o Common Language Runtime . Linguagens que direcionam a CLI compilam para CIL. CIL é um bytecode baseado em pilha e orientado a objetos . Tempos de execução normalmente just-in-time compilar instruções CIL em código nativo .

CIL era originalmente conhecida como Microsoft Intermediate Language (MSIL) durante as versões beta das linguagens .NET. Devido à padronização do C# e da CLI, o bytecode agora é oficialmente conhecido como CIL. [3] As definições de vírus do Windows Defender continuam a se referir aos binários compilados com ele como MSIL. [4]

informações gerais

Durante a compilação de linguagens de programação CLI , o código-fonte é traduzido em código CIL em vez de código de objeto específico de plataforma ou processador . CIL é um conjunto de instruções independente de plataforma e CPU que pode ser executado em qualquer ambiente que suporte a Common Language Infrastructure, como o tempo de execução .NET no Windows ou o tempo de execução Mono de plataforma cruzada . Em teoria, isso elimina a necessidade de distribuir diferentes arquivos executáveis ​​para diferentes plataformas e tipos de CPU. O código CIL é verificado quanto à segurança durante o tempo de execução, fornecendo melhor segurança e confiabilidade do que arquivos executáveis ​​compilados nativamente. [5] [6]

O processo de execução fica assim:

  1. O código-fonte é convertido em bytecode CIL e um assembly CLI é criado.
  2. Após a execução de um assembly CIL, seu código é passado pelo compilador JIT do tempo de execução para gerar o código nativo. A compilação antecipada também pode ser usada, o que elimina essa etapa, mas ao custo da portabilidade do arquivo executável.
  3. O processador do computador executa o código nativo.

Instruções

O bytecode CIL possui instruções para os seguintes grupos de tarefas:

Modelo computacional

A Common Intermediate Language é orientada a objetos e baseada em pilha , o que significa que os parâmetros de instrução e os resultados são mantidos em uma única pilha em vez de em vários registradores ou outros locais de memória, como na maioria das linguagens de programação .

Código que adiciona dois números em linguagem assembly x86 , onde eax e edx especificam dois registradores de uso geral diferentes :

adicionar eax , edx  

Código em uma linguagem intermediária (IL), onde 0 é eax e 1 é edx:

ldloc . 0 // coloca a variável local 0 na pilha ldloc . 1 // empurra a variável local 1 para a pilha add // abre e adiciona os dois primeiros itens da pilha e então empurra o resultado para a pilha stloc . 0 // abre e armazena o item do topo da pilha na variável local 0    
    
        
    

No último exemplo, os valores dos dois registradores, eax e edx, são primeiro colocados na pilha. Quando a instrução add é chamada, os operandos são "extraídos" ou recuperados, e o resultado é "empurrado" ou armazenado na pilha. O valor resultante é retirado da pilha e armazenado em eax.

Conceitos orientados a objetos

CIL é projetado para ser orientado a objetos. Você pode criar objetos, chamar métodos e usar outros tipos de membros, como campos.

Todo método precisa (com algumas exceções) residir em uma classe. O mesmo acontece com este método estático:

. classe pública Foo { . método public static int32 Add ( int32 , int32 ) cil managed { . maxstack 2 ldarg . 0 // carrega o primeiro argumento; ldarg . 1 // carrega o segundo argumento; adicionar // adicioná-los; ret // retorna o resultado; } }   
            
         
         
         
             
             
    

O método Add não requer que nenhuma instância de Foo seja declarada, pois ele é declarado como estático, podendo então ser utilizado assim em C#:

int r = Foo . Adicione ( 2 , 3 ); // 5        

No CIL ficaria assim:

ldc . i4 . 2ldc 
. _ i4 . 3 chame int32 Foo :: Adicione ( int32 , int32 ) stloc . 0
   

Classes de instância

Uma classe de instância contém pelo menos um construtor e alguns membros de instância . A classe a seguir tem um conjunto de métodos que representam ações de um objeto Car.

. classe pública Carro { . método public specialname rtspecialname instância void . ctor ( int32 , int32 ) cil managed { /* Construtor */ }   
              
        
    

    . method public void Move ( int32 ) cil managed { /* Omitindo implementação */ } . method public void TurnRight () cil managed { /* Omitindo implementação */ } . method public void TurnLeft () cil managed { /* Omitindo implementação */ } . method public void Brake () cil managed { /* Implementação omitida */        
            
            
            } 
}

Criando objetos

Em C# as instâncias de classe são criadas assim:

Carro meuCarro = novo Carro ( 1 , 4 ); Car yourCar = new Car ( 1 , 3 );      
     

E essas declarações são aproximadamente as mesmas que estas instruções no CIL:

ldc . i4 . 1ldc 
. _ i4 . 4 newobj instância void Car ::. ctor ( int , int ) stloc . 0 // meuCarro = new Carro(1, 4); ldc . i4 . 1ldc . _ i4 . 3 newobj instância void Car ::. ctor ( int , int ) stloc . 1 // seuCarro = new Carro(1, 3);
    
    


    
    

Invocando métodos de instância

Métodos de instância são invocados em C# como o seguinte:

meuCarro . Mover ( 3 );

Conforme invocado no CIL:

ldloc . 0 // Carrega o objeto "myCar" na pilha ldc . i4 . 3 chamam a instância void Car :: Move ( int32 )    

   

Metadados

A Common Language Infrastructure (CLI) registra informações sobre classes compiladas como metadados . Como a biblioteca de tipos no Component Object Model , isso permite que os aplicativos suportem e descubram as interfaces, classes, tipos, métodos e campos no assembly. O processo de leitura desses metadados é chamado de " reflexão ".

Os metadados podem ser dados na forma de "atributos". Os atributos podem ser customizados estendendo a Attributeclasse. Este é um recurso poderoso. Ele permite ao criador da classe a capacidade de adorná-la com informações extras que os consumidores da classe podem usar de várias maneiras significativas, dependendo do domínio do aplicativo.

Exemplo

Abaixo está um programa básico Hello, World escrito em CIL assembler. Ele exibirá a string "Hello, world!".

. montagem Olá {} . montagem externa mscorlib {} . método estático void Principal () { . ponto de entrada . maxstack 1 ldstr "Olá, mundo!" chamar void [ mscorlib ] System . Console :: WriteLine ( string ) ret }  
   
   

    
     
     
      
    

O código a seguir é mais complexo em número de opcodes.

Este código também pode ser comparado com o código correspondente no artigo sobre Java bytecode .

static void Main ( string [] args ) { for ( int i = 2 ; i < 1000 ; i ++ ) { for ( int j = 2 ; j < i ; j ++ ) { if ( i % j == 0 ) vá para o exterior ; } Console . WriteLine ( i );   

            
    
                
        
                  
                  
        
        
        externa :; 
} }    

Na sintaxe do montador CIL, fica assim:

. método private hidebysig static void Main ( string [] args ) cil managed { . ponto de entrada . maxstack 2 . locais init ( int32 V_0 , int32 V_1 )        

    
      
       
                   

              ldc . i4 .2 
stloc .0 br . s IL_001f IL_0004 : ldc . i4 .2 stloc .1 br . s IL_0011 IL_0008 : ldloc .0 ldloc .1 rem brfalse . s IL_001b ldloc .1 ldc . i4 .1 adicionar stloc .1 IL_0011 : ldloc .1 ldloc .0 blt . s IL_0008 ldloc              
                     
      
              
                     
      
              
              
                
              
              
              
              
      
              
                    
              .0 
chamada void [ mscorlib ] System . Console :: WriteLine ( int32 ) IL_001b : ldloc .0 ldc . i4 .1 adicionar stloc .0 IL_001f : ldloc .0 ldc . i4 0x3e8 blt . s IL_0004 ret }                      
      
              
              
              
      
                   
                    
              

Esta é apenas uma representação de como o CIL se parece próximo ao nível da máquina virtual (VM). Quando compilados os métodos são armazenados em tabelas e as instruções são armazenadas como bytes dentro do assembly, que é um Portable Executable (PE).

Geração

Um assembly CIL e instruções são gerados por um compilador ou um utilitário chamado IL Assembler ( ILAsm ) que é fornecido com o ambiente de execução.

O CIL montado também pode ser desmontado em código novamente usando o IL Disassembler (ILDASM). Existem outras ferramentas, como o .NET Reflector , que podem descompilar o CIL em uma linguagem de alto nível (por exemplo, C# ou Visual Basic ). Isso torna o CIL um alvo muito fácil para a engenharia reversa. Esta característica é compartilhada com o bytecode Java . No entanto, existem ferramentas que podem ofuscar o código e fazer isso de forma que o código não possa ser facilmente lido, mas ainda executável.

Execução

Compilação just-in-time

A compilação just-in-time (JIT) envolve transformar o código de byte em código imediatamente executável pela CPU. A conversão é realizada gradualmente durante a execução do programa. A compilação JIT fornece otimização específica do ambiente, segurança de tipo de tempo de execução e verificação de montagem. Para conseguir isso, o compilador JIT examina os metadados do assembly para quaisquer acessos ilegais e trata as violações de forma adequada.

Compilação antecipada

Os ambientes de execução compatíveis com CLI também vêm com a opção de fazer uma compilação antecipada (AOT) de um assembly para torná-lo mais rápido, removendo o processo JIT no tempo de execução.

No .NET Framework existe uma ferramenta especial chamada Native Image Generator (NGEN) que realiza o AOT. Uma abordagem diferente para AOT é o CoreRT , que permite a compilação do código .Net Core em um único executável sem dependência de um tempo de execução. No Mono também existe a opção de fazer um AOT.

Instruções de ponteiro - C++/CLI

Uma diferença notável do bytecode de Java é que CIL vem com ldind, stind, ldlocae muitas instruções de chamada que são suficientes para a manipulação de ponteiros de dados/funções necessária para compilar o código C/C++ em CIL.

classe A { public : virtual void __stdcall meth () {} }; void test_pointer_operations ( int param ) { int k = 0 ; int * ptr = & k ; * ptr = 1 ; ptr = & param ; * ptr = 2 ; A a ; A * ptra = &a  
        

   
	   
	    
	  
	  
	  
	 
	    ; 
ptra -> meth (); }	

O código correspondente em CIL pode ser renderizado assim:

. montagem do método estático void modopt ([ mscorlib ] System . Runtime . CompilerServices . CallConvCdecl ) test_pointer_operations ( int32 param ) cil managed { . vtentry 1 : 1 // Tamanho do código 44 (0x2c) . maxstack 2 . locais ([ 0 ] int32 * ptr , [ 1 ] tipo de valor A *     
           

     
  
    
     
              V_1 , 
[ 2 ] tipo de valor A * a , [ 3 ] int32 k ) // k = 0; IL_0000 : ldc . i4 .0 IL_0001 : stloc .3 // ptr = &k; IL_0002 : ldloca . s k // carrega a instrução de endereço do local IL_0004 : stloc .0 // *ptr = 1; IL_0005 : ldloc .0 IL_0006 : ldc . i4 .1              
             

     
    

        
    

    
    
  IL_0007 : stind . i4 // instrução de indireção // ptr = ¶m IL_0008 : ldarga . s param // carrega a instrução de endereço do parâmetro IL_000a : stloc .0 // *ptr = 2 IL_000b : ldloc .0 IL_000c : ldc . i4 .2 IL_000d : stind . i4 // a = novo A; IL_000e : ldloca . s a IL_0010 : chama valuetype A *   

        
    

    
    
    

       
             modopt ([ mscorlib ] System . Runtime . CompilerServices . CallConvThiscall ) ' A. { ctor } ' ( valuetype A * modopt ([ mscorlib ] System . Runtime . CompilerServices . IsConst ) modopt ([ mscorlib ] System . Runtime . CompilerServices . IsConst ) ) IL_0015 : pop    
    
// ptra = &a; 
IL_0016 : ldloca . s a IL_0018 : stloc .1 // ptra->meth(); IL_0019 : ldloc .1 IL_001a : dup IL_001b : ldind . i4 // lendo o VMT para chamada virtual IL_001c : ldind . i4 IL_001d : calli stdcall não gerenciado void modopt ([ mscorlib ] System . Runtime . CompilerServices .       
    

    
    
     
    
             CallConvStdcall )( native int ) IL_0022 : ret } // fim do método 'Global Functions'::test_pointer_operations 
    
 

Veja também

Referências

  1. ^ "Linguagem intermediária e execução" .
  2. ^ "ECMA-335 Common Language Infrastructure (CLI)" (PDF) . pág. 32.
  3. ^ "O que é Linguagem Intermediária(IL)/MSIL/CIL em .NET" . Recuperado 2011-02-17 . CIL: ... Quando compilamos [a]. NET, ele não [é] convertido diretamente para código binário, mas para a linguagem intermediária. Quando um projeto é executado, todas as linguagens de programação .NET são convertidas em código binário para CIL. Apenas uma parte do CIL que é necessária no tempo de execução é convertida em código binário. DLL e EXE de .NET também estão na forma CIL.
  4. ^ "HackTool:MSIL/SkypeCracker" . Microsoft . Acesso em 26 de novembro de 2019 .
  5. ^ Troelsen, Andrew (2009-05-02). Benefícios do CIL. ISBN 9781590598849. Recuperado 2011-02-17 .
  6. ^ "Extensões gerenciadas e não gerenciadas para C++, gerenciadas e .Net Framework". www.visualcplusdotnet.com . Recuperado 2020-07-07 .

Leitura adicional

  • Bock, Jason (2002). Programação CIL: sob o capô do .NET . Apress. ISBN 978-1590590416.

links externos

  • Infraestrutura de linguagem comum (padrão ECMA-335)
  • “ECMA C# e Common Language Infrastructure Standards” no site do Visual Studio
  • Olá, programa mundial em CIL
  • Velocidade: NGen aumenta seu desempenho com novos recursos poderosos -- MSDN Magazine, abril de 2005