Qual é a diferença entre código nativo, código de máquina e código assembly?

106

Estou confuso sobre o código de máquina e o código nativo no contexto das linguagens .NET.

Qual a diferença entre eles? Eles são os mesmos?

Samaladeepak
fonte
3
Eu tenho uma pergunta sobre esta questão. Esta pergunta se enquadra no requisito de StackOverflow? afaik não é, mas ao mesmo tempo esse tipo de pergunta é muito útil / informativa. Supondo que esse tipo de pergunta não seja permitido, onde devemos fazer esse tipo de pergunta se não for aqui?
Yousuf Azad

Respostas:

150

Os termos são realmente um pouco confusos, porque às vezes são usados ​​de forma inconsistente.

Código de máquina: Este é o mais bem definido. É um código que usa as instruções de código de byte que seu processador (a peça de metal física que faz o trabalho real) entende e executa diretamente. Todos os outros códigos devem ser traduzidos ou transformados em código de máquina antes que sua máquina possa executá-lo.

Código nativo: este termo às vezes é usado em lugares onde se refere o código de máquina (veja acima). No entanto, às vezes também é usado para significar código não gerenciado (veja abaixo).

Código não gerenciado e código gerenciado : o código não gerenciado se refere ao código escrito em uma linguagem de programação como C ou C ++, que é compilado diretamente no código de máquina . Ele contrasta com o código gerenciado , que é escrito em C #, VB.NET, Java ou similar e executado em um ambiente virtual (como .NET ou JavaVM) que meio que “simula” um processador em software. A principal diferença é que o código gerenciado “gerencia” os recursos (principalmente a alocação de memória) para você, empregando a coleta de lixo e mantendo as referências a objetos opacos. Código não gerenciadoé o tipo de código que requer que você aloque e desaloque manualmente a memória, às vezes causando vazamentos de memória (quando você se esquece de desalocar) e às vezes falhas de segmentação (quando você desaloca muito cedo). Não gerenciado também geralmente implica que não há verificações em tempo de execução para erros comuns, como desreferenciamento de ponteiro nulo ou estouro de limites de array.

Estritamente falando, a maioria das linguagens tipadas dinamicamente - como Perl, Python, PHP e Ruby - também são códigos gerenciados . No entanto, eles não são comumente descritos como tal, o que mostra que o código gerenciado é, na verdade, um termo de marketing para ambientes de programação comercial realmente grandes e sérios (.NET e Java).

Código assembly: este termo geralmente se refere ao tipo de código-fonte que as pessoas escrevem quando na verdade desejam escrever código de bytes. Um assembler é um programa que transforma esse código-fonte em um código de bytes real. Não é um compilador porque a transformação é de 1 para 1. No entanto, o termo é ambíguo quanto ao tipo de código de byte usado: ele pode ser gerenciado ou não gerenciado. Se não for gerenciado, o código de byte resultante é o código de máquina . Se for gerenciado, resulta no código de bytes usado nos bastidores por um ambiente virtual como o .NET. O código gerenciado (por exemplo, C #, Java) é compilado nesta linguagem de código de byte especial, que no caso do .NET é chamada Common Intermediate Language (CIL) e em Java é chamada de byte-code Java. Geralmente, há pouca necessidade para o programador comum acessar esse código ou escrever nessa linguagem diretamente, mas quando as pessoas o fazem, geralmente se referem a ele como código assembly porque usam um assembler para transformá-lo em código de bytes.

Timwi
fonte
C ++ pode ser compilado em código de máquina, mas muitas vezes é compilado em outros formatos, como exe, que será executado com um sistema operacional.
Gordon Gustafson
Existem linguagens que oferecem suporte à coleta de lixo e referências opacas que normalmente compilam para o código de máquina. As implementações mais sérias do Common Lisp fazem isso. O que você diz pode ser verdade para as linguagens com suporte da Microsoft, mas há mais linguagens compiladas do que as que são suportadas pelo Visual Studio.
David Thornley
3
@CrazyJugglerDrummer: O código contido em arquivos EXE gerados por compiladores C ++ ainda é um código de máquina. @David Thornley: Mencionei muito mais idiomas do que apenas esses, mas não queria complicar as coisas mencionando todas as raridades obscuras.
Timwi
Alguns compiladores, muitos, irão realmente compilar de C / C ++ ou outras linguagens para a linguagem assembly, em seguida, chamar o montador e o montador os transforma em arquivos-objeto que são principalmente código de máquina, mas precisam de alguns toques antes que possam ir para a memória do processador. o vinculador vincula tudo isso à versão em código de máquina do programa. O ponto sendo C / C ++, etc, muitas vezes não compila direto para o código de máquina, ele invisível para o usuário executa duas ou três etapas no caminho. O TCC, por exemplo, é uma exceção, ele vai diretamente para o código de máquina.
old_timer
Isso parece picuinhas, mas nem todos os montadores traduzem 1-1 em opcodes. Na verdade, muitos montadores modernos oferecem suporte a construções de abstração como classes. Exemplo: TASM, montador da Borland. en.wikipedia.org/wiki/TASM
Prime
45

O que você vê ao usar Debug + Windows + Disassembly ao depurar um programa C # é um bom guia para esses termos. Aqui está uma versão anotada dele quando compilo um programa 'hello world' escrito em C # na configuração da versão com a otimização JIT habilitada:

        static void Main(string[] args) {
            Console.WriteLine("Hello world");
00000000 55                push        ebp                           ; save stack frame pointer
00000001 8B EC             mov         ebp,esp                       ; setup current frame
00000003 E8 30 BE 03 6F    call        6F03BE38                      ; Console.Out property getter
00000008 8B C8             mov         ecx,eax                       ; setup "this"
0000000a 8B 15 88 20 BD 02 mov         edx,dword ptr ds:[02BD2088h]  ; arg = "Hello world"
00000010 8B 01             mov         eax,dword ptr [ecx]           ; TextWriter reference
00000012 FF 90 D8 00 00 00 call        dword ptr [eax+000000D8h]     ; TextWriter.WriteLine()
00000018 5D                pop         ebp                           ; restore stack frame pointer
        }
00000019 C3                ret                                       ; done, return

Clique com o botão direito na janela e marque "Mostrar bytes de código" para obter uma exibição semelhante.

A coluna à esquerda é o endereço do código da máquina. Seu valor é falsificado pelo depurador, o código está realmente localizado em outro lugar. Mas isso pode ser em qualquer lugar, dependendo do local selecionado pelo compilador JIT, então o depurador apenas começa a numerar os endereços de 0 no início do método.

A segunda coluna é o código da máquina . Os reais 1s e 0s que a CPU executa. O código de máquina, como aqui, é comumente exibido em hexadecimal. Talvez seja ilustrativo que 0x8B seleciona a instrução MOV, os bytes adicionais estão lá para dizer à CPU exatamente o que precisa ser movido. Observe também os dois sabores da instrução CALL, 0xE8 é a chamada direta, 0xFF é a instrução de chamada indireta.

A terceira coluna é o código de montagem . Assembly é uma linguagem simples, projetada para tornar mais fácil escrever código de máquina. Ele se compara ao C # sendo compilado para IL. O compilador usado para traduzir o código assembly é chamado de "assembler". Você provavelmente tem o Microsoft assembler em sua máquina, seu nome executável é ml.exe, ml64.exe para a versão de 64 bits. Existem duas versões comuns de linguagens assembly em uso. O que você vê é aquele que a Intel e a AMD usam. No mundo do código aberto, a montagem na notação AT&T é comum. A sintaxe da linguagem é fortemente dependente do tipo de CPU para a qual foi escrito, a linguagem assembly para um PowerPC é muito diferente.

Ok, isso aborda dois dos termos em sua pergunta. "Código nativo" é um termo difuso, não é incomum para descrever o código em uma linguagem não gerenciada. Talvez seja instrutivo ver que tipo de código de máquina é gerado por um compilador C. Esta é a versão 'hello world' em C:

int _tmain(int argc, _TCHAR* argv[])
{
00401010 55               push        ebp  
00401011 8B EC            mov         ebp,esp 
    printf("Hello world");
00401013 68 6C 6C 45 00   push        offset ___xt_z+128h (456C6Ch) 
00401018 E8 13 00 00 00   call        printf (401030h) 
0040101D 83 C4 04         add         esp,4 
    return 0;
00401020 33 C0            xor         eax,eax 
}
00401022 5D               pop         ebp  
00401023 C3               ret   

Eu não anotei, principalmente porque é muito semelhante ao código de máquina gerado pelo programa C #. A chamada da função printf () é bem diferente da chamada Console.WriteLine (), mas todo o resto é quase o mesmo. Observe também que o depurador agora está gerando o endereço do código de máquina real e que é um pouco mais inteligente sobre os símbolos. Um efeito colateral da geração de informações de depuração após gerar código de máquina, como costuma acontecer com os compiladores não gerenciados. Devo também mencionar que desativei algumas opções de otimização de código de máquina para fazer o código de máquina parecer semelhante. Os compiladores C / C ++ têm muito mais tempo disponível para otimizar o código; o resultado costuma ser difícil de interpretar. E muito difícil de depurar.

O ponto principal aqui é que há muito poucas diferenças entre o código de máquina gerado a partir de uma linguagem gerenciada pelo compilador JIT e o código de máquina gerado por um compilador de código nativo. Essa é a principal razão pela qual a linguagem C # pode ser competitiva com um compilador de código nativo. A única diferença real entre eles são as chamadas de função de suporte. Muitos dos quais são implementados no CLR. E isso gira principalmente em torno do coletor de lixo.

Hans Passant
fonte
6

Código nativo e código de máquina são a mesma coisa - os bytes reais que a CPU executa.

O código assembly tem dois significados: um é o código de máquina traduzido em uma forma mais legível (com os bytes para as instruções traduzidos em mnemônicos semelhantes a palavras como "JMP" (que "salta" para outro ponto no código). O outro é o bytecode IL (bytes de instrução que compiladores como C # ou VB geram, que acabarão sendo traduzidos em código de máquina eventualmente, mas ainda não são) que reside em uma DLL ou EXE.

cHao
fonte
2

Em .NET, os assemblies contêm código MS Intermediate Language (MSIL, às vezes CIL).
É como um código de máquina de 'alto nível'.

Quando carregado, o MSIL é compilado pelo compilador JIT em código nativo (código de máquina Intel x86 ou x64).

Henk Holterman
fonte