内核如何获得在linux下运行的可执行二进制文件?
这似乎是一个简单的问题,但任何人都可以帮助我深入挖掘?如何将文件加载到内存以及如何开始执行代码?
任何人都可以帮助我,一步一步地告诉我们发生了什么吗?
答案 0 :(得分:24)
Linux 4.0上exec
系统调用的最佳时刻
找到所有这些的最佳方法是使用QEMU GDB步骤调试内核:How to debug the Linux kernel with GDB and QEMU?
fs/exec.c
定义SYSCALL_DEFINE3(execve
简单地转发到do_execve
。
do_execve
转发至do_execveat_common
。
do_execveat_common
要查找下一个主要功能,请跟踪上次修改返回值retval
的时间。
开始构建struct linux_binprm *bprm
来描述该程序,并将其传递给exec_binprm
以执行。
exec_binprm
再次按照返回值查找下一个主要电话。
search_binary_handler
处理程序由可执行文件的第一个魔术字节确定。
两个最常见的处理程序是解释文件(#!
magic)和ELF(\x7fELF
magic)的处理程序,但内核中还有其他内置程序,例如: a.out
。用户也可以注册自己的/proc/sys/fs/binfmt_misc
ELF处理程序在fs/binfmt_elf.c
定义。
另请参阅:Why do people write the #!/usr/bin/env python shebang on the first line of a Python script?
formats
列表包含所有处理程序。
每个处理程序文件包含以下内容:
static int __init init_elf_binfmt(void)
{
register_binfmt(&elf_format);
return 0;
}
和elf_format
是该文件中定义的struct linux_binfmt
。
__init
是神奇的,并将该代码放入一个魔术部分,在内核启动时被调用:What does __init mean in the Linux kernel code?
链接器级依赖注入!
如果解释器无限地执行自身,还会有一个递归计数器。
试试这个:
echo '#!/tmp/a' > /tmp/a
chmod +x /tmp/a
/tmp/a
我们再次按照返回值查看接下来会发生什么,并看到它来自:
retval = fmt->load_binary(bprm);
其中load_binary
为结构上的每个处理程序定义:C风格的多态性。
fs/binfmt_elf.c:load_binary
实际工作是否正确:
struct linux_binprm
,注册为struct pt_regs
)start_thread
,这是真正开始安排的地方TODO:继续进行源分析。我期望接下来会发生什么:
/lib64/ld-linux-x86-64.so.2
)。dlopen
dlopen
使用可配置的搜索路径查找这些库(ldd
和朋友),将它们映射到内存,并以某种方式通知ELF在哪里找到其缺失的符号_start
否则,内核直接将可执行文件加载到内存中,而不使用动态加载程序。
因此,必须特别检查可执行文件是否为PIE,如果将其放在内存中的随机位置:What is the -fPIE option for position-independent executables in gcc and ld?
答案 1 :(得分:8)
system calls中的两个linux kernel是相关的。 fork系统调用(或者vfork
或clone
)用于创建新进程,类似于调用进程(除init
之外的每个Linux用户进程都是由fork
或朋友创建。 execve系统调用用一个新的进程地址空间替换(主要是从ELF可执行文件和匿名段中排序mmap段,然后初始化寄存器,包括堆栈指针)。 x86-64 ABI supplement和Linux assembly howto提供了详细信息。
动态链接发生在execve
之后,涉及/lib/x86_64-linux-gnu/ld-2.13.so
文件,ELF被视为“解释器”。
答案 2 :(得分:7)
阅读已经引用的ELF docs后,您应该read the kernel code实际执行此操作。
如果您无法理解该代码,请构建UML Linux,然后您可以在调试器中单步执行该代码。
答案 3 :(得分:2)
您可以从了解可执行文件格式(例如ELF)开始。 http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
ELF文件包含几个带标题的部分,这些部分描述了二进制文件应该如何以及在何处加载到内存中。
然后,我建议阅读linux中加载二进制文件并处理动态链接的部分ld-linux。这也是ld-linux的一个很好的描述:http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html