¿Cómo obtiene kernel un archivo ejecutable ejecutable bajo Linux?

¿Cómo obtiene kernel un archivo ejecutable ejecutable bajo Linux?

Parece una pregunta simple, pero ¿alguien puede ayudarme a cavar profundo? ¿Cómo se carga el archivo en la memoria y cómo se inicia el código de ejecución?

¿Alguien puede ayudarme y decir lo que está pasando paso a paso?

Los mejores momentos de la llamada del sistema exec en Linux 4.0

  • fs/exec.c define la llamada al sistema en SYSCALL_DEFINE3(execve

    Simplemente reenvía a do_execve .

  • do_execve

    Reenvía a do_execveat_common .

  • do_execveat_common

    Para encontrar la siguiente función principal, rastree cuándo se ha modificado por última vez el retorno del valor de retorno.

    Comienza a construir una struct linux_binprm *bprm para describir el progtwig y la pasa a exec_binprm para que se ejecute.

  • exec_binprm

    Una vez más, siga el valor de retorno para encontrar la próxima llamada importante.

  • search_binary_handler

    • Los manejadores están determinados por los primeros bytes mágicos del ejecutable.

      Los dos controladores más comunes son los de los archivos interpretados ( #! Magic) y de ELF ( \x7fELF magic), pero hay otros incorporados en el kernel, por ejemplo, a.out . Y los usuarios también pueden registrar sus propios aunque / proc / sys / fs / binfmt_misc

      El controlador ELF se define en fs/binfmt_elf.c .

    • La lista de formats contiene todos los controladores.

      Cada archivo de controlador contiene algo como:

       static int __init init_elf_binfmt(void) { register_binfmt(&elf_format); return 0; } 

      y elf_format es una struct linux_binfmt definida en ese archivo.

      __init es magia y pone ese código en una sección mágica que se llama cuando se inicia el kernel: ¿qué significa __init en el código del kernel de Linux?

      ¡Inyección de dependencia de nivel de enlazador!

    • También hay un contador de recursión, en caso de que un intérprete se ejecute infinitamente.

      Prueba esto:

       echo '#!/tmp/a' > /tmp/a chmod +x /tmp/a /tmp/a 
    • Una vez más, seguimos el valor de retorno para ver qué viene después, y vemos que proviene de:

       retval = fmt->load_binary(bprm); 

      donde load_binary se define para cada controlador en la estructura: polymorphism C-style.

  • fs/binfmt_elf.c:load_binary

    El trabajo real:

    • analiza el archivo ELF de acuerdo con las especificaciones
    • configura el estado del progtwig inicial del proceso basado en el ELF analizado (memoria en una struct linux_binprm , se registra en una struct pt_regs )
    • llama a start_thread , que es donde realmente puede comenzar a progtwigrse

TODO: continuar con el análisis de la fuente. Lo que espero que suceda a continuación:

  • el kernel analiza el encabezado INTERP de la ELF para encontrar el cargador dynamic (generalmente se establece en /lib64/ld-linux-x86-64.so.2 ).
  • el núcleo mmaps el cargador dynamic y el ELF para ser ejecutado en la memoria
  • el cargador dynamic se inicia, tomando un puntero al ELF en la memoria.
  • ahora en el dominio de usuario, el cargador de alguna manera analiza los encabezados de los elfos, y los deja dlopen
  • dlopen utiliza una ruta de búsqueda configurable para encontrar esas bibliotecas ( ldd y amigos), asignarlas a memoria mmap e informar de alguna manera al ELF dónde encontrar los símbolos que faltan
  • cargador llama a _start de ELF

Después de leer los documentos ELF ya mencionados, debería leer el código del kernel que realmente lo hace.

Si tiene problemas para entender ese código, cree un UML Linux , y podría pasar por ese código en el depurador.

Dos llamadas al sistema desde el kernel de Linux son relevantes. La llamada al sistema fork (o quizás vfork o clone ) se usa para crear un nuevo proceso, similar al de la llamada (cada proceso usuario-tierra Linux excepto que init es creado por fork o amigos). La llamada al sistema execve reemplaza el espacio de direcciones del proceso por uno nuevo (esencialmente por el tipo de segmentos mmap -ing del ejecutable ELF y segmentos anónimos, luego inicializando los registros, incluido el puntero de la stack). El suplemento x86-64 ABI y el ensamble de Linux cómo dar detalles.

La vinculación dinámica ocurre después de execve e involucra el archivo /lib/x86_64-linux-gnu/ld-2.13.so , que para ELF se ve como un “intérprete”.

Puede comenzar por comprender los formatos de archivo ejecutables, como ELF. http://en.wikipedia.org/wiki/Executable_and_Linkable_Format

El archivo ELF contiene varias secciones con encabezados que describen cómo y dónde se deben cargar partes del binario en la memoria.

Luego, sugiero leer de parte de Linux que carga binarios y maneja enlaces dynamics, ld-linux . Esta es también una buena descripción de ld-linux: http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html