2009-07-30 34 views
35

Me gustaría recopilar aquí lo que sucede cuando ejecuta un archivo ejecutable en Windows, Linux y OSX. En particular, me gustaría entender exactamente el orden de las operaciones: supongo que el kernel carga el formato de archivo ejecutable (PE, ELF o Mach-O), pero ignoro las diversas secciones del ELF (Ejecutable y Linkable Format) y su significado), y luego tiene el enlazador dinámico que resuelve las referencias, luego se ejecuta la parte __init del ejecutable, luego la principal, luego __fini, y luego se completa el programa, pero estoy seguro de que es muy duro, y probablemente equivocado.¿Qué sucede cuando ejecuta un programa?

Editar: la pregunta es ahora CW. Me estoy llenando de Linux. Si alguien quiere hacer lo mismo con Win y OSX, sería genial.

+1

¿Soy yo o el alcance de esta pregunta es demasiado amplio? – mezoid

+0

no creo que sea demasiado amplia, pero probablemente debería ser la comunidad wiki –

+0

me gustaría poner una recompensa por él si no tengo suficiente información. No podré hacerlo si es CW. –

Respuesta

0

Bueno, dependiendo de su definición exacta, debe tener en cuenta compiladores JIT para idiomas como .Net y Java. Cuando ejecuta un .Net "exe" que no es técnicamente "ejecutable", el compilador JIT entra y lo compila.

+3

El tiempo de ejecución .Net es un ejecutable ... El hecho que ejecuta un entorno virtual completo y optimiza bytecode es irrelevante. –

1

Tan pronto como la imagen se carga en la memoria, la magia toma el control.

+0

Eso es si lo tienes configurado en "Magia". "Más magia" rompe el universo. – jkeys

30

Esto es sólo en un nivel muy alto y abstracto por supuesto!

Executable - No Shared Libary: 

Client request to run application 
    ->Shell informs kernel to run binary 
    ->Kernel allocates memory from the pool to fit the binary image into 
    ->Kernel loads binary into memory 
    ->Kernel jumps to specific memory address 
    ->Kernel starts processing the machine code located at this location 
    ->If machine code has stop 
    ->Kernel releases memory back to pool 

Executable - Shared Library 

Client request to run application 
    ->Shell informs kernel to run binary 
    ->Kernel allocates memory from the pool to fit the binary image into 
    ->Kernel loads binary into memory 
    ->Kernel jumps to specific memory address 
    ->Kernel starts processing the machine code located at this location 
    ->Kernel pushes current location into an execution stack 
    ->Kernel jumps out of current memory to a shared memory location 
    ->Kernel executes code from this shared memory location 
    ->Kernel pops back the last memory location and jumps to that address 
    ->If machine code has stop 
    ->Kernel releases memory back to pool 

JavaScript/.NET/Perl/Python/PHP/Ruby (Interpretted Languages) 

Client request to run application 
    ->Shell informs kernel to run binary 
    ->Kernel has a hook that recognises binary images needs a JIT 
    ->Kernel calls JIT 
    ->JIT loads the code and jumps to a specific address 
    ->JIT reads the code and compiles the instruction into the 
    machine code that the interpretter is running on 
    ->Interpretture passes machine code to the kernel 
    ->kernel executes the required instruction 
    ->JIT then increments the program counter 
    ->If code has a stop 
    ->Jit releases application from its memory pool 

Como dice routeNpingme, los registros se establecen dentro de la CPU y ocurre la magia.

Actualización: ¡Sí, no puedo deletrear correctamente hoy!

+0

"El kernel carga el binario en la memoria -> El kernel salta a una dirección de memoria específica" "El kernel ejecuta el código desde esta ubicación de memoria compartida" Dudo que esto ocurra. El kernel es muy cuidadoso con el código que se ejecutará; normalmente, no ejecutará el código de espacio de usuario. Lo que dijiste puede ser fácilmente explotado por los atacantes. La respuesta de Stefano tiene mucho más sentido. – Infinite

21

Ok, respondiendo mi propia pregunta. Esto se hará progresivamente, y solo para Linux (y tal vez Mach-O). Siéntase libre de agregar más cosas a sus respuestas personales, para que se suban (y puede obtener insignias, ya que ahora es CW).

Comenzaré a la mitad, y construiré el resto cuando lo descubra. Este documento se ha realizado con un x86_64, gcc (GCC) 4.1.2.

Al abrir el archivo, la inicialización

En esta sección, se describe lo que ocurre cuando se invoca el programa, desde el punto de vista del núcleo, hasta que el programa está listo para ser ejecutado.

  1. El ELF está abierto.
  2. el kernel busca la sección .text y la carga en la memoria. Lo marca como readonly
  3. el kernel carga la sección .data
  4. el kernel carga la sección .bss e inicializa todo el contenido a cero.
  5. el kernel transfiere el control al enlazador dinámico (cuyo nombre está dentro del archivo ELF, en la sección .interp). El vinculador dinámico resuelve todas las llamadas de biblioteca compartidas.
  6. el control se transfiere a la aplicación

La ejecución del programa

  1. la _start función se invoca, como la cabecera de ELF especifica como el punto de entrada para el ejecutable
  2. _start llamadas __libc_start_main en glibc (a través del PLT) que pasa a la siguiente información para que

    1. el anuncio vestido de la función principal real
    2. la dirección de argc
    3. la dirección de argv
    4. la dirección de la _init rutina
    5. la dirección del puntero _fini rutina
    6. función para el registro atexit()
    7. la dirección de pila más alta disponible
  3. _init se llama

    1. llamadas call_gmon_start para inicializar perfiles gmon. no realmente relacionado con la ejecución.
    2. llamadas frame_dummy, que envuelve __register_frame_info (eh_frame sección de dirección, sección bss dirección) (FIXME: ¿qué hace esta función inicializa VARs global de la sección BSS aparentemente)
    3. llamadas __do_global_ctors_aux, cuya función es llamar a todo el mundial constructores listados en la sección de .ctores.
  4. principal se llama a
  5. extremos principales
  6. _fini es llamada, que a su vez llamadas __do_global_dtors_aux para ejecutar todos los destructores como se especifica en la sección .dtors.
  7. el programa sale.
+1

No sé en qué detalle desea entrar, pero tengo problemas para seguir esto porque no sé qué es un ELF. (Bueno, o eso o Linux es muy diferente bajo el capó de lo que imaginaba) –

+0

Continuaré esta parte tan pronto como tenga tiempo para seguir leyendo los documentos que encontré. ELF es un formato binario para ejecutables bajo Linux. Es como PE en ganar y Mach-O en OSX –

3

En Windows, primero la imagen se carga en memoria. El kernel analiza qué bibliotecas (leer "DLL") va a requerir y las carga también.

A continuación, edita la imagen del programa para insertar las direcciones de memoria de cada una de las funciones de biblioteca que requiere. Estas direcciones ya tienen un espacio en el binario .EXE, pero simplemente están llenas de ceros. procedimiento

de cada DLL DllMain() es ejecutado a continuación, uno a uno, desde el DLL más necesario para el último, al igual que a raíz de una orden de dependencias.

Una vez que todas las bibliotecas se cargaron y se prepararon, finalmente, se inicia la imagen, y cualquier cosa que suceda ahora dependerá de lenguaje utilizado, compilador usado, y el programa de rutina en sí.

Cuestiones relacionadas