内核如何获得在Linux下运行的可执行二进制文件?
这似乎是一个简单的问题,但任何人都可以帮我深挖? 如何将文件加载到内存以及如何开始执行代码?
任何人都可以帮助我,并一步一步告诉发生了什么?
Linux 4.0上exec
系统调用的最佳时刻
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
定义。
formats
列表包含所有的处理程序。
每个处理程序文件包含如下所示
static int __init init_elf_binfmt(void) { register_binfmt(&elf_format); return 0; }
而elf_format
是在该文件中定义的struct linux_binfmt
。
__init
是神奇的,并将这些代码放入一个魔术部分,当内核启动时会被调用: __init在Linux内核代码中的含义是什么?
链接器级别的依赖注入!
还有一个递归计数器,以防解释器无限地执行。
尝试这个:
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
,这是真正开始计划的地方 待办事项:进一步继续进行来源分析。 我期望接下来会发生什么:
/lib64/ld-linux-x86-64.so.2
头以找到动态加载器(通常设置为/lib64/ld-linux-x86-64.so.2
)。 dlopen
dlopen
使用可配置的搜索路径来查找这些库( ldd
和friends),将它们映射到内存中,并以某种方式通知ELF在哪里找到其丢失的符号 _start
来自linux内核的两个系统调用是相关的。 fork系统调用(或者vfork
或者clone
)被用来创建一个新的进程,类似于调用的进程(除了init
之外,每个Linux用户登陆进程都由fork
或者朋友创建)。 execve系统调用用一个新的进程地址空间替换进程地址空间(主要是通过ELF可执行文件和匿名段的mmap-段),然后初始化寄存器,包括堆栈指针。 x86-64 ABI补充和Linux组件如何提供细节。
动态链接发生在execve
之后,涉及/lib/x86_64-linux-gnu/ld-2.13.so
文件,ELF被视为“解释器”。
阅读已经引用的ELF文档后,您应该只是读取实际执行的内核代码 。
如果您在理解代码时遇到困难,请构建一个UML Linux ,然后您可以在调试器中遍历该代码。
您可以从理解可执行文件格式(如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