2009-05-10 21 views
37

Estoy interesado principalmente en compiladores populares y ampliamente utilizados, como gcc. Pero si las cosas se hacen de manera diferente con diferentes compiladores, me gustaría saber eso también.¿Los compiladores de lenguaje de programación se traducen primero al ensamblaje o directamente al código de máquina?

Tomando gcc como un ejemplo, lo hace compilar un corto programa escrito en C directamente a código de máquina o tiene la primera traducirlo al ensamblaje legible por humanos, y sólo entonces se utiliza un (incorporada?) Ensamblador para traducir el programa de ensamblaje a código binario, código - ¿una serie de instrucciones para la CPU?

¿Está usando el código de ensamblado para crear un ejecutable binario una operación significativamente costosa? ¿O es algo relativamente simple y rápido de hacer?

(Supongamos que estamos tratando con sólo la familia de procesadores x86, y todos los programas están escritos para Linux.)

estaría muy agradecido por cualquier ayuda y el pensamiento sobre el asunto. ¡Gracias!

Respuesta

40

gcc en realidad produce el ensamblador y lo ensambla usando el ensamblador como. No todos los compiladores hacen esto: los compiladores de MS producen código de objeto directamente, aunque puede hacer que generen salida de ensamblador. Traducir el ensamblador al código objeto es un proceso bastante simple, al menos en comparación con la compilación.

Algunos compiladores producen otro código de lenguaje de alto nivel como su salida - por ejemplo, cfront, el primer compilador de C++ producido C como su salida que luego fue compilado por un compilador C.

Tenga en cuenta que ni la compilación directa ni el ensamblaje realmente producen un ejecutable. Esto se hace mediante el linker, que toma los diversos archivos de código objeto producidos por compilación/ensamblaje, resuelve todos los nombres que contienen y produce el binario final ejecutable.

+3

Algunos compiladores históricos solían producir ejecutables directamente. Algunos incluso podrían escribir un archivo .COM ejecutable en una sola pasada durante la compilación [siguiendo el código de cada procedimiento, el compilador podría generar una lista de puntos de parche dentro de ese procedimiento junto con la dirección de la lista de puntos de parche del procedimiento anterior; el código de inicio podría hacer todos los parches necesarios cuando se cargue el código]. Esto hizo posible la compilación rápida en un espacio de memoria muy pequeño, incluso cuando se usan disquetes. – supercat

6

Los compiladores, en general, analizan el código fuente en un Árbol de sintaxis abstracta (AST), y luego en un lenguaje intermedio. Solo entonces, generalmente después de algunas optimizaciones, emiten el idioma de destino.

Acerca de gcc, puede compilar a una amplia variedad de objetivos. No sé si para x86 se compila para ensamblar primero, pero le di una idea de los compiladores, y usted también lo pidió.

1

Visual C++ tiene un switch para generar el código de ensamblado, por lo que creo que genera código de ensamblaje antes de generar el código de máquina.

6

Según chapter 2 de Introduction to Reverse Engineering Software (por Mike Perry y Nasko Oskov), tanto gcc y cl.exe (el compilador de fondo para MSVC++) tienen el interruptor -S se puede utilizar para dar salida a la asamblea que produce cada compilador .

También puede ejecutar gcc en modo detallado (gcc -v) para obtener una lista de los comandos que ejecuta para ver lo que está haciendo detrás de las escenas.

1

probablemente estaría interesado en escuchar este podcast: Internals of GCC

+1

Enlace actualizado: http://www.se-radio.net/2007/07/episode-61-internals-of-gcc/ –

1

En el lenguaje se genera más multi-pass compilers conjunto durante las etapas de generación de código.Esto le permite escribir el lexer, la sintaxis y las fases semánticas una vez y luego generar el código ejecutable usando un solo backend ensamblador. esto se usa mucho en compiladores cruzados, tales como compiladores de C, que se generan para una gama de diferentes CPU.

Casi todos los compiladores tienen alguna forma de esto si es un paso implicito o explicito.

5

GCC compila para el ensamblador. Algunos otros compiladores no. Por ejemplo, LLVM-GCC compila a LLVM-assembly o LLVM-bytecode, que luego se compila en código máquina. Casi todos los compiladores tienen algún tipo de representación interna, LLVM-GCC usa LLVM y, IIRC, GCC usa algo llamado GIMPLE.

0

Los compiladores de Java compilan el código de byte de Java (formato binario) y luego lo ejecutan usando una máquina virtual (jvm).

Si bien esto puede parecer lento, puede ser más rápido porque la JVM puede aprovechar las posteriores instrucciones de la CPU y las nuevas optimizaciones. Un compilador de C++ no hará esto: tiene que apuntar al conjunto de instrucciones en tiempo de compilación.

14

Casi todos los compiladores, incluido gcc, producen código de ensamblado porque es más fácil --- tanto para producir como para depurar el compilador. Las principales excepciones suelen ser compiladores just-in-time o compiladores interactivos, cuyos autores no desean la sobrecarga del rendimiento o la molestia de bifurcar todo un proceso para ejecutar el ensamblador. Algunos ejemplos interesantes incluyen

  • Standard ML of New Jersey, que se ejecuta de forma interactiva y compila toda expresión sobre la marcha.

  • tinycc compiler, que está diseñado para ser lo suficientemente rápido como para compilar, cargar y ejecutar una secuencia de comandos C en menos de 100 milisegundos, y por lo tanto no desea la sobrecarga de llamar al ensamblador y al enlazador.

Lo que estos casos tienen en común es el deseo de una respuesta "instantánea". Los ensambladores y los enlazadores son bastante rápidos, pero no lo suficientemente buenos para una respuesta interactiva. Todavía.

También hay una gran familia de idiomas, como Smalltalk, Java y Lua, que se compilan en bytecode, no en código ensamblador, pero cuyas implementaciones pueden traducir ese bytecode directamente al código máquina sin el beneficio de un ensamblador.

(Nota al pie: a principios de 1990, María Fernández y yo escribimos la New Jersey Machine Code Toolkit, para lo cual el code es en línea, lo que genera C   bibliotecas que los autores de compiladores pueden utilizar para evitar el ensamblador y enlazador estándar María usó para más o menos. duplicar la velocidad de su enlazador de optimización al generar a.out. Si no escribe en el disco, las aceleraciones son aún mayores ...)

1

Hay muchas fases de compilación. En abstracto, está el front-end que lee el código fuente, lo divide en tokens y finalmente en un árbol de análisis sintáctico.

El extremo posterior es responsable de generar primero un código secuencial como código de tres direcciones por ejemplo:

código:

x = y + z + w 

en:

reg1 = y + z 
x = reg1 + w 

Entonces optimizarlo, traducirla en montaje y finalmente en lenguaje de máquina. Todos los pasos están en capas cuidadosamente para que cuando sea necesario, uno de ellos puede ser reemplazado

0

Aunque todos los compiladores no convierten el código fuente en un código de nivel intermedio pero hay un puente de tomar el código fuente a código de máquina en varios compiladores

2

Ninguna de las respuestas aclara el hecho de que un ensamblador es la primera capa de abstracción entre el código binario y dependiente de la máquina código simbólico. Un compilador es la segunda capa de abstracción entre el CÓDIGO SÍMBOLO DEPENDIENTE DE LA MÁQUINA y el CÓDIGO SÍMBOLO INDEPENDIENTE DE LA MÁQUINA.

Si un compilador convierte directamente el código al código binario, por definición, se llamará al ensamblador y no un compilador.

Es más apropiado decir que un compilador utiliza código intermedio que puede o no puede ser, por ejemplo, el lenguaje ensamblador Java usa el código de bytes como código intermedio y el código de bytes es ensamblador para la máquina virtual Java (JVM).

EDIT: Usted puede preguntarse por qué un ensamblador siempre produce código máquina dependiente y por qué un compilador es capaz de producir código máquina independiente. La respuesta es muy simple. Un ensamblador es un mapeo directo del código de máquina y, por lo tanto, el lenguaje de ensamblaje que produce depende siempre de la máquina. Por el contrario, podemos escribir más de una versión de un compilador para diferentes máquinas. Entonces, para ejecutar nuestro código independientemente de la máquina, debemos compilar el mismo código pero en la versión del compilador escrita para esa máquina.

Cuestiones relacionadas