2009-12-31 13 views
6

Soy un programador novato. Solo quería ver el resultado en la compilación de diferentes fases, ensamblando el enlace &. No sé el lenguaje ensamblador también.¿Qué sucede con los identificadores en un programa?

me escribió un programa sencillo

#include <stdio.h> 

int humans = 9; 

int main() 
{ 
     int lions = 2; 
     int cubs = populate(lions); 
     return 0; 
} 

int populate(int crappyVariable) 
{ 
    return ++crappyVariable; 
} 

Solía ​​gcc - S sample.c Estoy sorprendido por la salida del lenguaje ensamblador. Perdí todos los nombres de variables & nombres de funciones.

conservó los identificadores globales como humanos, poblar, principal pero los prefiguró con subrayados _. Entonces, no lo consideraré como usar identificadores. De todos modos, punto es que perdió todos los identificadores.

Mi pregunta es ¿cómo podría llamar a las funciones o consultar las variables?

Tengo mucha curiosidad sobre otras etapas de la producción, que serían en binario (que no se puede ver).

¿Cómo sería la salida justo después de ensamblar & antes de vincular? Supongo que perderá incluso los identificadores globales prefijados bajo guión también? Entonces otra vez la pregunta es ¿cómo llamaría a funciones o se referiría a variables para operaciones?

Busqué información en internet pero no encontré nada útil. Puede ser que no estoy seguro de qué buscar. No quiero leer libros grandes sobre esto. Pero si hay artículos, tutoriales que aclaran conceptos. Eso también sería útil.

Soy un programador novato. Por lo tanto, sería genial que pueda explicar en términos simples pero técnicos.

EDIT: En respuesta, al comentario. Rompí mi pregunta en múltiples preguntas. Aquí está la segunda parte de esta pregunta: not clear with the job of the linker

+4

Es posible que tenga mejor suerte con esto si separarlo en diferentes cuestiones. Cubrir todo este terreno en una sola respuesta es probablemente más de lo que la mayoría de la gente, incluido yo, está dispuesta a hacer en su tiempo libre. – tvanfosson

+1

Lo siento. No pensé en eso. Estaba tan confundido. Así que seguí escribiendo. Trataré de dividirme en varias publicaciones. – Alice

Respuesta

3

En el nivel básico de la máquina, no hay más nombres, solo direcciones numéricas para las variables y el código. Por lo tanto, una vez que su código se traduce al lenguaje de máquina, los nombres desaparecen por razones prácticas.

Si compila con una opción "ensamblar" o desensambla código, puede ver algunos identificadores; están ahí para ayudarlo a encontrar el camino correcto en el código, ya que no se espera que esté computando innecesariamente las compensaciones de datos/códigos en su cabeza.

Para responder a su pregunta sobre los enlaces y cosas por el estilo: Las etiquetas y los identificadores que solo se usan "dentro" de un archivo de programa C desaparecen una vez que el programa se compila en forma de objeto reubicable. Sin embargo, los nombres definidos externamente, como main() son necesarios porque los módulos externos harán referencia a ellos; por lo tanto, un archivo de objeto compilado contendrá una pequeña tabla que enumerará los nombres visibles externamente y a qué ubicación se refieren. Un enlazador puede parchar referencias externas en su módulo de otros (y viceversa) basándose en esos nombres.

Después del enlace, incluso los nombres definidos externamente ya no se necesitan. Sin embargo, si compila con opciones de depuración, las tablas de nombres aún pueden estar adjuntas al programa final, por lo que puede usar esos nombres al depurar su programa.

2

Realmente necesita leer acerca de los compiladores y el diseño del compilador. Comience con http://www.freetechbooks.com/compiler-design-and-construction-f14.html

Aquí está el resumen.

El objetivo es obtener material copiado en la memoria que se ejecutará y ejecutará. Entonces el sistema operativo le da el control a esas cosas.

El "cargador" copia cosas en la memoria de varios archivos. Estos archivos son en realidad un tipo de lenguaje que describe dónde van las cosas en la memoria y qué sucede en esos lugares. Es una especie de lenguaje de "memoria de carga".

El trabajo de compilador y enlazador es crear archivos que harán que el cargador haga lo correcto.

La salida del compilador es archivos "objeto" - esencialmente instrucciones del cargador en muchos archivos pequeños fragmentados con muchas referencias externas. La salida del compilador es, idealmente, algún código de máquina con marcadores de posición para que las referencias externas se conecten. Todas las referencias internas se han resuelto como compensaciones en la memoria del montón o marcos de pila o nombres de funciones.

La salida del enlazador es archivos de cargador más grandes con menos referencias externas. Es en gran medida el mismo que el resultado del compilador en formato. Pero tiene más cosas doblado en

Leer esto en el comando ld:. http://linux.about.com/library/cmd/blcmdl1_ld.htm

Leer esto en el comando nm: http://linux.about.com/library/cmd/blcmdl1_nm.htm

He aquí algunos detalles.

"... ¿cómo llamaría a las funciones o se referiría a las variables?"

Los nombres de las funciones, en general, se conservan hasta las últimas etapas de producción de salida.

Los nombres de las variables se transforman en algo más. Las variables "globales" se asignan estáticamente y el compilador tiene un mapa del nombre de la variable al tipo para desplazarse a la memoria estática ("montón").

Las variables locales dentro de una función se asignan (generalmente) en el marco de la pila. El compilador tiene un mapa del nombre de la variable a escribir para desplazar al marco de la pila. Cuando se ingresa la función, se asigna un marco de pila del tamaño requerido y las variables simplemente se compensan en ese marco.

"... ¿cómo llamaría a funciones o se referiría a variables para operaciones?"

Debe proporcionar una pista al compilador. La palabra clave extern le dice al compilador que un nombre no está definido en este módulo, pero está definido en otro módulo y la referencia debe resolverse en el momento del enlace (o carga).

"... si no hay nada para vincular ..."

Esto nunca es cierto. Tu programa es solo una parte del ejecutable general. La mayoría de las bibliotecas C incluyen el programa principal real que luego llama a su función llamada "main".

"¿El enlazador cambiará la salida del código de objeto del ensamblador?"

Esto varía mucho con el sistema operativo. En muchos sistemas operativos, el enlazador y la carga ocurren todos a la vez.Lo que sucede a menudo es que la salida del compilador de C se envía a un archivo sin que realmente se haya realizado demasiada resolución.

Cuando el archivo ejecutable se carga en la memoria, también se cargan las referencias de archivo y cualquier archivo de objeto compartido externo.

"El programa no se está ejecutando, solo está en la etapa de fabricación".

Esto no significa nada. No estoy seguro de por qué estás incluyendo esto.

"¿Cómo podría enlazar el mapa a la memoria? ¿Cómo se vería?"

El sistema operativo asignará un bloque de memoria en el que se debe copiar el programa ejecutable. El vinculador/cargador lee el archivo de objeto, cualquier archivo de archivo de objeto y copia las cosas en esos archivos en esa memoria. El enlazador realiza la copia y la resolución del nombre y escribe un nuevo archivo de objeto que es más compilador. El cargador lo convierte en memoria real y convierte la ejecución en la página de texto resultante.

"Está en el tiempo de ejecución ¿verdad?"

Esa es la única manera de depurar: tiempo de ejecución. No puede significar nada más, o no está depurando.

+0

"El programa no se está ejecutando, solo está en la etapa de fabricación". Lo que quiero decir es que el programa c que escribí aún no se encuentra en la etapa de ejecución.Todavía está en camino de convertirse en un ejecutable – Alice

+0

Bueno, su archivo es un archivo de código fuente, o se ha compilado a un archivo de código objeto, o se ha vinculado a un archivo ejecutable. –

+0

@Alice: Eso sigue siendo irrelevante; todo esto sucede mucho antes de que el programa comience a funcionar. Decir que no se está ejecutando no es útil. –

0

Para ver cómo las variables locales se manejan en el código ensamblador, compilar algo como:

int main() { int foo = 42; } 

Lo que notará es que no sólo el nombre de la variable desaparece, pero donde los datos resultantes va. Verá algo como:

movq %rsp, %rbp 

que establece el puntero base en el puntero de la pila actual. Entonces:

movl $42, -4(%rbp) 

Entonces, lo que esto nos dice es que el compilador asigna un espacio en la pila, pero lo deja sin nombre. Agregar más variables como foo básicamente solo asignará más memoria debajo del puntero base. La variable que era "foo" ahora es solo -4(%rbp).

0

Un buen paso siguiente es ejecutar objdump -D en el archivo .o generado y compararlo con la versión .S.

Eso da una idea de lo que es .S es el maquillaje, y lo que se traduce en binario.

La etapa final es la vinculación, que más o menos significa dos pasadas para resolver todas las etiquetas entre múltiples archivos .o a direcciones relativas a 0 o una dirección de carga.

ver la gran enlazadores libres y Cargadores libro http://www.iecc.com/linker/ para obtener más información sobre la vinculación de

+0

eso es un gran consejo. Seguramente lo haré. Quiero aprender más sobre esto. – Alice

Cuestiones relacionadas