Pregunta

Escribo programas vacíos para molestar muchísimo a los codificadores de stackoverflow, NO.Recién estoy explorando la cadena de herramientas de GNU.

Ahora bien, lo siguiente puede ser demasiado profundo para mí, pero para continuar con la saga de programas vacíos he comenzado a examinar la salida del compilador de C, el material que consume GNU.

gcc version 4.4.0 (TDM-1 mingw32)

prueba.c:

int main()
{
    return 0;
}

gcc -S prueba.c

    .file   "test.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .text
.globl _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    call    ___main
    movl    $0, %eax
    leave
    ret 

¿Puedes explicar qué sucede aquí?Aquí está mi esfuerzo por entenderlo.he usado el as manual y mis conocimientos mínimos de ASM x86:

  • .file "test.c" es la directiva para el nombre de archivo lógico.
  • .def:según los documentos "Comience a definir información de depuración para el nombre de un símbolo".¿Qué es un símbolo (un nombre de función/variable?) y qué tipo de información de depuración?
  • .scl:los doctores dicen "La clase de almacenamiento puede indicar si un símbolo es estático o externo".¿Es esto lo mismo? estático y externo ¿Lo sé por C?¿Y qué es ese '2'?
  • .type:almacena el parámetro "como atributo de tipo de una entrada de la tabla de símbolos", No tengo ni idea.
  • .endef:ningún problema.
  • .text:Ahora bien, esto es problemático, parece ser algo llamado sección y he leído que es el lugar para el código, pero los documentos no me dijeron demasiado.
  • .globl "hace que el símbolo sea visible para ld"., el manual es bastante claro al respecto.
  • _main: Esta podría ser la dirección inicial (?) de mi función principal
  • pushl_:Un push largo (32 bits), que coloca a EBP en la pila
  • movl:Movimiento de 32 bits.Pseudo-C: EBP = ESP;
  • andl:Y lógico.Pseudo-C: ESP = -16 & ESP, Realmente no veo cuál es el punto de esto.
  • call:Empuja la IP a la pila (para que el procedimiento llamado pueda encontrar el camino de regreso) y continúa donde __main es.(¿Qué es __principal?)
  • movl:este cero debe ser la constante que devuelvo al final de mi código.El MOV coloca este cero en EAX.
  • leave:restaura la pila después de una instrucción ENTER (?).¿Por qué?
  • ret:vuelve a la dirección de instrucción que está guardada en la pila

¡Gracias por su ayuda!

¿Fue útil?

Solución

  

.FILE "test.c"

Comandos comenzando con. son directivas para el ensamblador. Esto sólo dice que esto es "file.c", esa información puede ser exportada a la información de depuración del exe.

  

.def ___main; .SCL 2;   .type 32; .endef

.def directivas define un símbolo de depuración. scl 2 significa la clase de almacenamiento 2 (clase de almacenamiento externo) .Type 32 dice que este sumbol es una función. Estos números serán definidos por el pe-coff exe formato

___ principal es una función llamada que se encarga de que las necesidades de bootstrapping gcc (que va a hacer cosas como correr inicializadores estáticos C ++ y otra limpieza es necesario).

.text

da comienzo a una sección de texto -. Código vive aquí

  

.globl _main

define el símbolo _main como global, que hará que sea visible para el enlazador y a otros módulos que está vinculada en.

.def        _main;  .scl    2;      .type   32;     .endef

Lo mismo que _main, crea símbolos de depuración que indica que es una función _main. Esto puede ser usado por los depuradores.

  

_main:

Inicia una nueva etiqueta (Se va a terminar una dirección). la directiva .globl anterior hace esta dirección visible para otras entidades.

pushl       %ebp

países del viejo puntero de marco (registro ebp) en la pila (por lo que puede volver a poner en su lugar cuando termina esta función)

movl        %esp, %ebp

Mueve el puntero de pila en el registro ebp. ebp a menudo se llama el puntero de marco, que apunta en la parte superior de los valores de la pila dentro del "marco" actual (por lo general la función), (en referencia a las variables en la pila a través de ebp puede ayudar depuradores)

  

andl $ -16% esp

Ands la pila con fffffff0 que se alinea effectivly que en un límite de 16 bytes. El acceso a los valores alineados en la pila son mucho más rápido que si fueran alineados. Todas estas instrucciones anteriores son más o menos un prólogo de función estándar.

call        ___main

llama a la función ___main que hacer cosas inicialización que las necesidades de gcc. Llamada empujará el puntero de instrucción actual en la pila y salta a la dirección del ___ principal

movl        $0, %eax

mover 0 al registro eax, (el 0 en return 0;). El registro eax se utiliza para mantener los valores de retorno de función para la convención de llamada stdcall

  

licencia

La instrucción de la licencia es más o menos la abreviatura de

movl     ebp,esp
popl     ebp

es decir. it "deshacer" la materia hecha en el inicio de la función -. restaurando el puntero de marco y la pila a su estado anterior

  

ret

Vuelve a quien llama a esta función. Se hará estallar el puntero de instrucción de la pila (el cual una instrucción de solicitud correspondiente se habrá colocado allí) y saltar allí.

Otros consejos

Hay un ejercicio muy similar se describe aquí: http://en.wikibooks.org/wiki/ X86_Assembly / GAS_Syntax

Se ha descubierto la mayor parte de ella - Voy a tomar notas adicionales para el énfasis y adiciones

.

__main es una subrutina en la biblioteca estándar de GNU que se encarga de varios inicialización de arranque. No es estrictamente necesario para los programas en C, pero se requiere en caso de que el código C es la vinculación con C ++.

_main es su subrutina principal. Dado que tanto _main y __main son ubicaciones de código que tienen la misma clase y tipo de almacenamiento. Yo aún no he desenterrado las definiciones para .scl y .type todavía. Usted puede conseguir un poco de iluminación mediante la definición de unas pocas variables globales.

Las primeras tres instrucciones son la creación de un marco de pila, que es un término técnico para el almacenamiento de trabajo de una subrutina - variables locales y temporales en su mayor parte. Empujar ebp salva la base del marco de pila de la persona que llama. Poner en esp ebp establece la base de nuestro marco de pila. El andl alinea el marco de pila a un límite de 16 bytes por si acaso las variables locales en la pila requieren 16 alineación de bytes (para las arquitecturas x86 instrucciones SIMD requieren que la alineación, pero de alineación no acelerar tipos ordinarios tales como ints y floats.

En este punto se esperaría normalmente esp para conseguir mueve hacia abajo en la memoria para asignar espacio de pila para las variables locales. Su main tiene ninguna tan gcc no molesta.

La llamada a __main es especial para el punto de entrada principal y no aparecerá típicamente en subrutinas.

El resto va a medida que habrá deducido. Register eax es el lugar para poner los códigos de retorno de enteros en la especificación binaria. leave deshace el marco de pila y ret se remonta a la persona que llama. En este caso, la persona que llama es el tiempo de ejecución de bajo nivel C, que va a hacer mágico adicional (como llamar a las funciones atexit(), establecer el código de salida para el proceso y pedir al sistema operativo para terminar el proceso.

En cuanto a que andl $ -16% esp

  • 32 bits: -16 en decimal es igual a 0xfffffff0 en representación hexadecimal
  • 64 bits: -16 en decimal es igual a 0xfffffffffffffff0 en representación hexadecimal

Así que será enmascarar los últimos 4 bits de ESP. (Por cierto: 2 ** 4 es igual a 16) y conservará todos los demás bits (no importa si el sistema de destino es de 32 o 64 bits)

Además de la andl $-16,%esp, esto funciona porque el establecimiento de los bits de baja a cero se ajuste siempre %esp por en el valor, y la pila crece hacia abajo en x86.

No tengo todas las respuestas pero puedo explicar lo que sé.

ebp es utilizado por la función para almacenar el estado inicial de esp durante su flujo, una referencia a dónde se pasan los argumentos a la función y dónde están sus propias variables locales.Lo primero que hace una función es guardar el estado del dado ebp haciendo pushl %ebp, es vital para la función que realiza la llamada y luego la reemplaza por su propia posición actual en la pila esp haciendo movl %esp, %ebp.Poner a cero los últimos 4 bits de ebp en este punto es específico de GCC, no sé por qué este compilador hace eso.Funcionaría sin hacerlo.Ahora finalmente entramos en el negocio, call ___main, ¿Quién es __principal?Yo tampoco lo sé...tal vez más procedimientos específicos de GCC y, finalmente, lo único que hace su main() es establecer el valor de retorno en 0 con movl $0, %eax y leave que es lo mismo que hacer movl %ebp, %esp; popl %ebp para restaurar ebp estado, entonces ret para terminar. ret estallido eip y continuar el flujo del hilo desde ese punto, dondequiera que esté (como es main(), este ret probablemente conduce a algún procedimiento del núcleo que maneja el final del programa).

La mayor parte se trata de gestionar la pila.Escribí un tutorial detallado sobre cómo se usa la pila hace algún tiempo, sería útil explicar por qué se hacen todas esas cosas.Pero está en portugués...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top