How does kernel get an executable binary file running under linux?
It seems a simple question, but anyone can help me dig deep? How the file is loaded to memory and how execution code get started?
Can anyone help me and tell what's happening step by step?
Two system calls from the linux kernel are relevant. The fork system call (or perhaps
vfork
orclone
) is used to create a new process, similar to the calling one (every Linux user-land process exceptinit
is created byfork
or friends). The execve system call replace the process address space by a fresh one (essentially by sort-of mmap-ing segments from the ELF executable and anonymous segments, then initializing the registers, including the stack pointer). The x86-64 ABI supplement and the Linux assembly howto give details.The dynamic linking happens after
execve
and involves the/lib/x86_64-linux-gnu/ld-2.13.so
file, which for ELF is viewed as an "interpreter".You can start by understanding executable file formats, such as ELF. http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
The ELF file contains several sections with headers that describes how and where parts of the binary should be loaded into memory.
Then, I suggest reading up on the part of linux that loads binaries and handles dynamic linking, ld-linux. This is also a good description of ld-linux: http://www.cs.virginia.edu/~dww4s/articles/ld_linux.html
Best moments of the
exec
system call on Linux 4.0The best way to find all of that out is to GDB step debug the kernel with QEMU: How to debug the Linux kernel with GDB and QEMU?
fs/exec.c
defines the system call atSYSCALL_DEFINE3(execve
Simply forwards to
do_execve
.do_execve
Forwards to
do_execveat_common
.do_execveat_common
To find the next major function, track when return value
retval
is last modified.Starts building a
struct linux_binprm *bprm
to describe the program, and passes it toexec_binprm
to execute.exec_binprm
Once again, follow the return value to find the next major call.
search_binary_handler
Handlers are determined by the first magic bytes of the executable.
The two most common handlers are those for interpreted files (
#!
magic) and for ELF (\x7fELF
magic), but there are other built-into the kernel, e.g.a.out
. And users can also register their own though /proc/sys/fs/binfmt_miscThe ELF handler is defined at
fs/binfmt_elf.c
.See also: Why do people write the #!/usr/bin/env python shebang on the first line of a Python script?
The
formats
list contains all the handlers.Each handler file contains something like:
and
elf_format
is astruct linux_binfmt
defined in that file.__init
is magic and puts that code into a magic section that gets called when the kernel starts: What does __init mean in the Linux kernel code?Linker-level dependency injection!
There is also a recursion counter, in case an interpreter executes itself infinitely.
Try this:
Once again we follow the return value to see what comes next, and see that it comes from:
where
load_binary
is defined for each handler on the struct: C-style polymorsphism.fs/binfmt_elf.c:load_binary
Does the actual work:
struct linux_binprm
, registers into astruct pt_regs
)start_thread
, which is where it can really start to getting scheduledTODO: continue source analysis further. What I expect to happen next:
/lib64/ld-linux-x86-64.so.2
).dlopen
on themdlopen
uses a configurable search path to find those libraries (ldd
and friends), mmap them to memory, and somehow inform the ELF where to find its missing symbols_start
of the ELFAfter reading the ELF docs already referenced, you should just read the kernel code that actually does it.
If you have trouble understanding that code, build a UML Linux, and you could step through that code in the debugger.