O C compila para Linguagem de Máquina ou Assembly?

Amigos,
To sem projeto…atoa…e bateu uma dúvida xD

O C compila para Assembly ou Linguagem de Máquina?
O arquivo “.exe” gerado, dentro dele…tem linguagem de máquina que consegue conversar direto com o hardware do computador ou Assembly?

Me falavam que o arquivo “.exe” gerado pelo C era compilação da linguagem para Assembly…
Porém, “minha visão e talves ignorancia do momento é:”
Assembly ainda não é linguagem de máquina e sim uma intermediaria de nivel mais baixo para que nós possamos entender, e se o .exe fosse assembly o processadorr não ia intender o.O. Sendo assim, o C compila para limguagem de máquina e não assembly?

Isso também quer dizer que Placas microcontroladoras que entendem assembly, na verdade não entendem…e sim possuem um compilador para transformar o assembly em linguagem de máquina real para depois entender?

Pessoal, quem for responder e me ajudar com as dúvidas por favor tente explicar de forma racional e detalhista e não só postar algo objetivo sem fundamento…por favor hehe.

‘‘Linguagens como C e C++ são compiladas estaticamente, e seus códigos fontes são transformados diretamente em linguagem de máquina.’’ by http://henriquebastos.net/2008/09/06/diferencas-entre-linguagem-compilada-e-linguagem-interpretada/

o assembly é na verdade um passo para a linguagem de máquina. Isso quer dizer que as instruções são mostradas como mnemônicos para fins de visualização. Por exemplo:

a instrução move seria -> 001 // isso é uma suposição

move x, 10 poderia ser

então o montador converte o mnemônico para :

001 x 10

É um exemplo grosseiro de como funciona.

e só uma correção do que você falou - > Linguagem de Alto nível -> Compilador -> Linguagem Assembly -> Montador -> Linguagem de Máquina, no caso não é o compilador responsável por transformar linguagem assembly em linguagem de máquina e sim o Montador(Assembler).
Valeu :smiley:

Importante ressaltar também que o .EXE é um formato de executavel hoje utilizado no Windows, e há outras particularidades no processo de compilação e não simplesmente uma tradução de alto nivel -> assembly -> máquina… inclusive há processos de otimização durante a compilação.

Uma forma interessante de entender isso é baixar um disassembler (por exemplo o ollydbg no windows), criar um programa bobo em C, compilar e depois abrir no disassembler para ver o código assembly encima do código de máquina gerado pelo compilador.

E microcontroladores não entendem Assembly, eles entendem linguagem de máquina…

O compilador C converte o programa original em C para uma forma intermediária (que depende muito do compilador e que muitas vezes não depende do processador.).

Essa forma intermediária nem sempre aparece em arquivos, às vezes é só construída em memória.

Exemplos de compiladores que usam a forma intermediária: o gcc (onde a forma intermediária pode ser usada também por compiladores Java, C++, Ada etc. - e nesse caso temos o gcj, g++, gnat etc.) e o clang (compilador da Apple, que usa uma forma intermediária chamada LLVM)… A forma intermediária gerada pelo compilador da Microsoft não está documentada publicamente, mas também tem essa finalidade (é algo que é relativamente independente do processador final, que pode ser x86, x64, MIPS, ARM etc.)

Essa forma intermediária, por sua vez, pode ser convertida para Assembly (ou seja, uma listagem com extensão .asm ou .s ) ou então diretamente para as instruções do processador. Depende muito das opções que você passa para o compilador.
o compilador
Como a forma intermediária é algo que proporciona muitas oportunidades para otimização do programa completo, muitas vezes o compilador só gera o formato intermediário, e é o linker que, ao juntar os diversos arquivos .obj (ou .o), chama a fase final do compilador para a partir da forma intermediária, fazer a otiimização global do programa, e então gerar o código de máquina.

[quote=entanglement]O compilador C converte o programa original em C para uma forma intermediária (que depende muito do compilador e que muitas vezes não depende do processador.).

Essa forma intermediária nem sempre aparece em arquivos, às vezes é só construída em memória.

Exemplos de compiladores que usam a forma intermediária: o gcc (onde a forma intermediária pode ser usada também por compiladores Java, C++, Ada etc. - e nesse caso temos o gcj, g++, gnat etc.) e o clang (compilador da Apple, que usa uma forma intermediária chamada LLVM)… A forma intermediária gerada pelo compilador da Microsoft não está documentada publicamente, mas também tem essa finalidade (é algo que é relativamente independente do processador final, que pode ser x86, x64, MIPS, ARM etc.)

Essa forma intermediária, por sua vez, pode ser convertida para Assembly (ou seja, uma listagem com extensão .asm ou .s ) ou então diretamente para as instruções do processador. Depende muito das opções que você passa para o compilador.
o compilador
Como a forma intermediária é algo que proporciona muitas oportunidades para otimização do programa completo, muitas vezes o compilador só gera o formato intermediário, e é o linker que, ao juntar os diversos arquivos .obj (ou .o), chama a fase final do compilador para a partir da forma intermediária, fazer a otiimização global do programa, e então gerar o código de máquina. [/quote]

Após isso ainda existe o “loader” que carrega os programas do disco para a memória quando são executados.

existe um gráfico legal que mostra isso:

http://www.tenouk.com/ModuleW.html

Existe outra coisa interessante que queria mostrar, mas não me lembro onde li sobre isso. Java hotspot usa llvm. Vou ver se encontro o artigo e posto aqui.

[quote=jmmenezes]Importante ressaltar também que o .EXE é um formato de executavel hoje utilizado no Windows, e há outras particularidades no processo de compilação e não simplesmente uma tradução de alto nivel -> assembly -> máquina… inclusive há processos de otimização durante a compilação.

Uma forma interessante de entender isso é baixar um disassembler (por exemplo o ollydbg no windows), criar um programa bobo em C, compilar e depois abrir no disassembler para ver o código assembly encima do código de máquina gerado pelo compilador.
[/quote]

Reforçando o que você disse, um executável nada mais é que uma estrutura de dados “específica” de plataforma. Uma estrutura que inclui registros que guardam versão, sistema e também o programa em si que é o assembly

[code]ELF Header:

Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00

Class: ELF32

Data: 2’s complement, little endian

Version: 1 (current)

OS/ABI: UNIX - System V

ABI Version: 0

Type: REL (Relocatable file)

Machine: Intel 80386

Version: 0x1

Entry point address: 0x0

Start of program headers: 0 (bytes into file)

Start of section headers: 672 (bytes into file)

Flags: 0x0

Size of this header: 52 (bytes)

Size of program headers: 0 (bytes)

Number of program headers: 0

Size of section headers: 40 (bytes)

Number of section headers: 11

Section header string table index: 8

Section Headers:

[Nr] Name Type Addr Off Size ES Flg Lk Inf Al

[ 0] NULL 00000000 000000 000000 00 0 0 0

[ 1] .text PROGBITS 00000000 000034 0000de 00 AX 0 0 4

[ 2] .rel.text REL 00000000 00052c 000068 08 9 1 4

[ 3] .data PROGBIT 00000000 000114 000000 00 WA 0 0 4

[ 4] .bss NOBIT 00000000 000114 000000 00 WA 0 0 4

[ 5] .rodata PROGBITS 00000000 000114 00010a 00 A 0 0 4

[ 6] .note.GNU-stack PROGBITS 00000000 00021e 000000 00 0 0 1

[ 7] .comment PROGBITS 00000000 00021e 000031 00 0 0 1

[ 8] .shstrtab STRTAB 00000000 00024f 000051 00 0 0 1

[ 9] .symtab SYMTAB 00000000 000458 0000b0 10 10 9 4

[10] .strtab STRTAB 00000000 000508 000021 00 0 0 1

Key to Flags:

W (write), A (alloc), X (execute), M (merge), S (strings)

I (info), L (link order), G (group), x (unknown)

O (extra OS processing required) o (OS specific), p (processor specific)

There are no program headers in this file.

Relocation section ‘.rel.text’ at offset 0x52c contains 13 entries:

Offset Info Type Sym.Value Sym. Name

0000002d 00000501 R_386_32 00000000 .rodata

00000032 00000a02 R_386_PC32 00000000 printf

00000044 00000501 R_386_32 00000000 .rodata

00000049 00000a02 R_386_PC32 00000000 printf

0000005c 00000501 R_386_32 00000000 .rodata

00000061 00000a02 R_386_PC32 00000000 printf

0000008c 00000501 R_386_32 00000000 .rodata

0000009c 00000501 R_386_32 00000000 .rodata

000000a1 00000a02 R_386_PC32 00000000 printf

000000b3 00000501 R_386_32 00000000 .rodata

000000b8 00000a02 R_386_PC32 00000000 printf

000000cb 00000501 R_386_32 00000000 .rodata

000000d0 00000a02 R_386_PC32 00000000 printf

There are no unwind sections in this file.

Symbol table ‘.symtab’ contains 11 entries:

Num: Value Size Type Bind Vis Ndx Name

 0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

 1: 00000000     0 FILE    LOCAL  DEFAULT  ABS testprog1.c

 2: 00000000     0 SECTION LOCAL  DEFAULT    1

 3: 00000000     0 SECTION LOCAL  DEFAULT    3

 4: 00000000     0 SECTION LOCAL  DEFAULT    4

 5: 00000000     0 SECTION LOCAL  DEFAULT    5

 6: 00000080     94 FUNC   LOCAL  DEFAULT    1 display

 7: 00000000     0 SECTION LOCAL  DEFAULT    6

 8: 00000000     0 SECTION LOCAL  DEFAULT    7

 9: 00000000     128 FUNC  GLOBAL DEFAULT    1 main

10: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

No version information found in this file.[/code]

No caso de um microcontrolador, onde o programa não costuma ser carregado por um “Loader” e sim ficar presente em uma determinada região de memória apenas de leitura (EEPROM, ROM, memória FLASH etc.) você precisa de um programa adicional que já deixa todas as referências fixas no código a ser “queimado” nessa memória apenas de leitura.