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?
Después de leer los documentos ELF que ya están referenciados, simplemente debes leer el código del kernel que realmente lo hace.
Si tiene problemas para entender ese código, compile un UML Linux y puede pasar por ese código en el depurador.
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".
Puede comenzar por entender los formatos de archivos 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.
Entonces, sugiero leer sobre la parte de linux que carga binarios y maneja enlaces dinámicos, ld-linux . Esta es también una buena descripción de ld-linux: http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html
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 enSYSCALL_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 aexec_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_miscEl 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 unastruct 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 unastruct 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