users texto operaciones ejemplos convertir con comandos comando binarios binario basicos archivos archivo c linux

texto - ¿Cómo obtiene el kernel un archivo binario ejecutable ejecutándose en linux?



convertir archivo binario a texto linux (4)

¿Cómo obtiene el kernel un archivo binario ejecutable ejecutándose en linux?

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

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



Dos llamadas al sistema desde el kernel de linux son relevantes. La llamada al sistema de fork (o tal vez vfork o clone ) se usa para crear un nuevo proceso, similar al que llama (todos los procesos de usuario de Linux excepto el init son creados por fork o amigos). La execve llamada al sistema reemplaza el espacio de direcciones del proceso por uno nuevo (esencialmente por una especie de segmentos de mmap desde el ejecutable ELF y los segmentos anónimos, luego inicializando los registros, incluido el puntero de pila). El complemento ABI x86-64 y el ensamblaje de Linux dan detalles.

El enlace dinámico 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".



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

La mejor manera de descubrir todo eso es hacer un paso GDB para depurar el kernel con QEMU: ¿Cómo depurar el kernel de Linux con GDB y QEMU?

  • 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, retval seguimiento de la última vez que se modifique el valor de retorno.

    Comienza a construir una struct linux_binprm *bprm para describir el programa y lo 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 para ELF ( /x7fELF magic), pero hay otros incorporados en el kernel, por ejemplo, a.out . Y los usuarios también pueden registrar sus propios /proc/sys/fs/binfmt_misc

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

      Ver también: ¿Por qué las personas escriben #! / Usr / bin / env python shebang en la primera línea de un script de Python?

    • La lista de formats contiene todos los manejadores.

      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 mágico y coloca ese código en una sección mágica a la que se llama cuando se inicia el kernel: ¿Qué significa __init en el código del kernel de Linux?

      Inyección de dependencia a nivel de vinculador!

    • 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 a continuación, y vemos que viene de:

      retval = fmt->load_binary(bprm);

      donde load_binary se define para cada controlador en la estructura: polimorfismo de estilo C

  • fs/binfmt_elf.c:load_binary

    ¿El trabajo real:

    • analiza el archivo ELF de acuerdo con las especificaciones
    • configura el estado del programa inicial del proceso en función del ELF analizado (la memoria en una struct linux_binprm , se registra en una struct pt_regs )
    • llame a start_thread , que es donde realmente puede comenzar a programarse

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

  • el kernel analiza el encabezado INTERP del ELF para encontrar el cargador dinámico (generalmente establecido en /lib64/ld-linux-x86-64.so.2 ).
  • el kernel se ajusta al cargador dinámico y el ELF que se ejecutará en la memoria
  • Se inicia el cargador dinámico, tomando un puntero al ELF en la memoria.
  • ahora en userland, el cargador de alguna manera analiza los encabezados elfos, y hace dlopen en ellos
  • dlopen usa una ruta de búsqueda configurable para encontrar esas bibliotecas ( ldd y amigos), las coloca en la memoria e informa al ELF de dónde encontrar los símbolos que faltan
  • El cargador llama a _start del ELF