内核如何获得在Linux下运行的可执行二进制文件?
内核如何获得在Linux下运行的可执行二进制文件?
这似乎是一个简单的问题,但任何人都可以帮我深挖? 如何将文件加载到内存以及如何开始执行代码?
任何人都可以帮助我,并一步一步告诉发生了什么?
Linux 4.0上exec
系统调用的最佳时刻
-
fs/exec.c
在SYSCALL_DEFINE3(execve
定义系统调用简单地转发到
do_execve
。 -
do_execve
转发到
do_execveat_common
。 -
do_execveat_common
要查找下一个主要函数,请跟踪返回值
retval
最后修改的时间。开始构build一个
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内核代码中的含义是什么?链接器级别的dependency injection!
-
还有一个recursion计数器,以防解释器无限地执行。
尝试这个:
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
是否实际工作:
- 根据规格parsingELF文件
- 根据parsing的ELF(内存到一个
struct linux_binprm
,注册到一个struct pt_regs
)设置进程的初始程序状态。 - 调用
start_thread
,这是真正开始计划的地方
待办事项:进一步继续进行来源分析。 我期望接下来会发生什么:
- 内核分析ELF的
/lib64/ld-linux-x86-64.so.2
头以finddynamic加载器(通常设置为/lib64/ld-linux-x86-64.so.2
)。 - 内核将dynamic加载器和要执行的ELF映射到内存
- 启动dynamic加载程序,在内存中带一个指向ELF的指针。
- 现在在userland,加载器以某种方式parsing精灵标题,并且对它们进行
dlopen
-
dlopen
使用可configuration的searchpath来查找这些库(ldd
和friends),将它们映射到内存中,并以某种方式通知ELF在哪里find其丢失的符号 - 加载器调用ELF的
_start
来自linux内核的两个系统调用是相关的。 fork系统调用(或者vfork
或者clone
)被用来创build一个新的进程,类似于调用的进程(除了init
之外,每个Linux用户登陆进程都由fork
或者朋友创build)。 execve系统调用用一个新的进程地址空间replace进程地址空间(主要是通过ELF可执行文件和匿名段的mmap-段的sorting,然后初始化寄存器,包括堆栈指针)。 x86-64 ABI补充和Linux组件如何提供细节。
dynamic链接发生在execve
之后,涉及/lib/x86_64-linux-gnu/ld-2.13.so
文件,ELF被视为“解释器”。
阅读已经引用的ELF文档后,您应该只是读取实际执行的内核代码 。
如果您在理解代码时遇到困难,请构build一个UML Linux ,然后您可以在debugging器中遍历该代码。
您可以从理解可执行文件格式(如ELF)开始。 http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
ELF文件包含几个标题部分,描述应该如何将部分二进制文件加载到内存中。
然后,我build议读一下加载二进制文件和处理dynamic链接的linux部分, ld-linux 。 这也是对ld-linux的一个很好的描述: http : //www.cs.virginia.edu/~dww4s/articles/ld_linux.html