The Code of FS/binfmt_elf.c is as follows:
Static int load_elf_binary (struct linux_binprm * bprm, struct pt_regs * regs)
{
Struct file * interpreter = NULL;/* To shut GCC up */
Unsigned long load_addr = 0, load_bias = 0;
Int load_addr_set = 0;
Char * elf_interpreter = NULL;
Unsigned long error;
Struct elf_phdr * elf_ppnt, * elf_phdata;
Unsigned long elf_bss, elf_brk;
Int retval, I;
Unsigned int size;
Unsigned long elf_entry;
Unsigned long interp_load_addr = 0;
Unsigned long start_code, end_code, start_data, end_data;
Unsigned long reloc_func_desc = 0;
Int executable_stack = exstack_default;
Unsigned long def_flags = 0;
Struct {
Struct elfhdr elf_ex;
Struct elfhdr interp_elf_ex;
} * LOC;
Loc = kmalloc (sizeof (* LOC), gfp_kernel );
If (! Loc ){
Retval =-enomem;
Goto out_ret;
}
Loc-> elf_ex = * (struct elfhdr *) bprm-> BUF );
Retval =-enoexec;
If (memcmp (loc-> elf_ex.e_ident, elfmag, selfmag )! = 0)
Goto out;
/* Determine whether the program is executable or shared library. If not, return */
If (loc-> elf_ex.e_type! = Et_exec & loc-> elf_ex.e_type! = Et_dyn)
Goto out;
If (! Elf_check_arch (& loc-> elf_ex ))
Goto out;
If (! Bprm-> file-> f_op |! Bprm-> file-> f_op-> MMAP)
Goto out;
/* If the size of the read program header is different from that of the elf standard program header, the system returns */
If (loc-> elf_ex.e_phentsize! = Sizeof (struct elf_phdr ))
Goto out;
/* The number of program headers is invalid */
If (loc-> elf_ex.e_phnum <1 |
Loc-> elf_ex.e_phnum> 65536u/sizeof (struct elf_phdr ))
Goto out;
Size = loc-> elf_ex.e_phnum * sizeof (struct elf_phdr );
Retval =-enomem;
/* Allocate space for the program header */
Elf_phdata = kmalloc (size, gfp_kernel );
If (! Elf_phdata)
Goto out;
/* Read the content of the header of the executable file program, and finally call the READ function of VFS */
Retval = kernel_read (bprm-> file, Loc-> elf_ex.e_phoff,
(Char *) elf_phdata, size );
If (retval! = Size ){
If (retval> = 0)
Retval =-EIO;
Goto out_free_ph;
}
/* BSS segment, BRK segment first initialized to 0 */
Elf_ppnt = elf_phdata;
Elf_bss = 0;
Elf_brk = 0;
Start_code = ~ 0ul;
End_code = 0;
Start_data = 0;
End_data = 0;
For (I = 0; I <loc-> elf_ex.e_phnum; I ++ ){
If (elf_ppnt-> p_type = pt_interp ){
/* Find the corresponding dynamic linker name from the pt_interp segment and load the dynamic linker. Usually it is/lib/ld-linux.so.2 .*/
Retval =-enoexec;
If (elf_ppnt-> p_filesz> path_max |
Elf_ppnt-> p_filesz <2)
Goto out_free_ph;
Retval =-enomem;
/* Allocate space for the dynamic connector and read and load it */
Elf_interpreter = kmalloc (elf_ppnt-> p_filesz,
Gfp_kernel );
If (! Elf_interpreter)
Goto out_free_ph;
Retval = kernel_read (bprm-> file, elf_ppnt-> p_offset,
Elf_interpreter,
Elf_ppnt-> p_filesz );
If (retval! = Elf_ppnt-> p_filesz ){
If (retval> = 0)
Retval =-EIO;
Goto out_free_interp;
}
/* Make sure path is NULL terminated */
Retval =-enoexec;
If (elf_interpreter [elf_ppnt-> p_filesz-1]! = '\ 0 ')
Goto out_free_interp;
/* Set the elf Executable File features */
Set_personality (loc-> elf_ex );
/* The kernel sets some tag pairs in the stack of the new process to indicate operations related to the dynamic linker. For details, see open_exec implementation */
Interpreter = open_exec (elf_interpreter );
Retval = ptr_err (Interpreter );
If (is_err (Interpreter ))
Goto out_free_interp;
/*
* If the binary is not readable then enforce
* Mm-> dumpable = 0 regardless of the interpreter's
* Permissions.
*/
If (file_permission (Interpreter, may_read) <0)
Bprm-> interp_flags | = binprm_flags_enforce_nondump;
Retval = kernel_read (Interpreter, 0, bprm-> Buf,
Binprm_buf_size );
If (retval! = Binprm_buf_size ){
If (retval> = 0)
Retval =-EIO;
Goto out_free_dentry;
}
Loc-> interp_elf_ex = * (struct elfhdr *) bprm-> BUF );
Break;
}
/* Check all program headers cyclically to see if dynamic connectors exist */
Elf_ppnt ++;
}
Elf_ppnt = elf_phdata;
For (I = 0; I <loc-> elf_ex.e_phnum; I ++, elf_ppnt ++)
If (elf_ppnt-> p_type = pt_gnu_stack ){
If (elf_ppnt-> p_flags & pf_x)
Executable_stack = exstack_enable_x;
Else
Executable_stack = exstack_disable_x;
Break;
}
/* Check whether a dynamic connector is used. Whether a dynamic connector exists, the ELF file is executed */
If (elf_interpreter ){
Retval =-elibbad;
/* Not an elf interpreter */
If (memcmp (loc-> interp_elf_ex.e_ident, elfmag, selfmag )! = 0)
Goto out_free_dentry;
/* Verify the interpreter has a valid arch */
If (! Elf_check_arch (& loc-> interp_elf_ex ))
Goto out_free_dentry;
} Else {
/* Executables without an interpreter also need a personality */
Set_personality (loc-> elf_ex );
}
/* All related code of the parent process is cleared here */
Retval = flush_old_exec (bprm );
If (retval)
Goto out_free_dentry;
/* OK, this is the point of no return */
Current-> flags & = ~ Pf_forknoexec;
Current-> MM-> def_flags = def_flags;
/* Do this immediately, since stack_top as used in setup_arg_pages
May depend on the personality .*/
Set_personality (loc-> elf_ex );
If (elf_read_implies_exec (loc-> elf_ex, executable_stack ))
Current-> personality | = read_implies_exec;
If (! (Current-> personality & addr_no_randomize) & randomize_va_space)
Current-> flags | = pf_randomize;
Arch_pick_mmap_layout (current-> mm );
/* Do this so that we can load the interpreter, if need be. We will
Change some of these later */
Current-> MM-> free_area_cache = Current-> MM-> mmap_base;
Current-> MM-> cached_hole_size = 0;
/* Run the following dynamic connector to obtain the kernel space page */
Retval = setup_arg_pages (bprm, randomize_stack_top (stack_top ),
Executable_stack );
If (retval <0 ){
Send_sig (sigkill, current, 0 );
Goto out_free_dentry;
}
Current-> MM-> start_stack = bprm-> P;
/* Cyclically load all executable files to the memory according to the previously obtained program header table */
For (I = 0, elf_ppnt = elf_phdata;
I <loc-> elf_ex.e_phnum; I ++, elf_ppnt ++ ){
Int elf_prot = 0, elf_flags;
Unsigned long K, vaddr;
If (elf_ppnt-> p_type! = Pt_load)
Continue;
If (unlikely (elf_brk> elf_bss )){
Unsigned long nbyte;
/* There Was A pt_load segment with p_memsz> p_filesz
Before this one. Map anonymous pages, if needed,
And clear the area .*/
Retval = set_brk (elf_bss + load_bias,
Elf_brk + load_bias );
If (retval ){
Send_sig (sigkill, current, 0 );
Goto out_free_dentry;
}
Nbyte = elf_pageoffset (elf_bss );
If (nbyte ){
Nbyte = elf_min_align-nbyte;
If (nbyte> elf_brk-elf_bss)
Nbyte = elf_brk-elf_bss;
If (clear_user (void _ User *) elf_bss +
Load_bias, nbyte )){
/*
* This BSS-zeroing can fail if the elf
* File specifies odd protections. So
* We don't check the return value
*/
}
}
}
If (elf_ppnt-> p_flags & pf_r)
Elf_prot | = prot_read;
If (elf_ppnt-> p_flags & pf_w)
Elf_prot | = prot_write;
If (elf_ppnt-> p_flags & pf_x)
Elf_prot | = prot_exec;
Elf_flags = map_private | map_denywrite | map_executable;
Vaddr = elf_ppnt-> p_vaddr;
If (loc-> elf_ex.e_type = et_exec | load_addr_set ){
Elf_flags | = map_fixed;
} Else if (loc-> elf_ex.e_type = et_dyn ){
/* Try and get dynamic programs out of the way of
* Default MMAP base, as well as whatever program they
* Might try to Exec. This is because the BRK will
* Follow the loader, and is not movable .*/
# Ifdef config_x86
Load_bias = 0;
# Else
Load_bias = elf_pagestart (elf_et_dyn_base-vaddr );
# Endif
}
Error = elf_map (bprm-> file, load_bias + vaddr, elf_ppnt,
Elf_prot, elf_flags, 0 );
If (bad_addr (error )){
Send_sig (sigkill, current, 0 );
Retval = is_err (void *) error )?
Ptr_err (void *) error):-einval;
Goto out_free_dentry;
}
If (! Load_addr_set ){
Load_addr_set = 1;
Load_addr = (elf_ppnt-> p_vaddr-elf_ppnt-> p_offset );
If (loc-> elf_ex.e_type = et_dyn ){
Load_bias + = Error-
Elf_pagestart (load_bias + vaddr );
Load_addr + = load_bias;
Reloc_func_desc = load_bias;
}
}
K = elf_ppnt-> p_vaddr;
If (k <start_code)
Start_code = K;
If (start_data <K)
Start_data = K;
/*
* Check to see if the Section's size will overflow
* Allowed task size. Note that p_filesz must always be
* <= P_memsz so it is only necessary to check p_memsz.
*/
If (bad_addr (k) | elf_ppnt-> p_filesz> elf_ppnt-> p_memsz |
Elf_ppnt-> p_memsz> task_size |
Task_size-elf_ppnt-> p_memsz <k ){
/* Set_brk can never work. Avoid overflows .*/
Send_sig (sigkill, current, 0 );
Retval =-einval;
Goto out_free_dentry;
}
K = elf_ppnt-> p_vaddr + elf_ppnt-> p_filesz;
If (k> elf_bss)
Elf_bss = K;
If (elf_ppnt-> p_flags & pf_x) & end_code <K)
End_code = K;
If (end_data <K)
End_data = K;
K = elf_ppnt-> p_vaddr + elf_ppnt-> p_memsz;
If (k> elf_brk)
Elf_brk = K;
}
/* Update the records of the Information read into the memory */
Loc-> elf_ex.e_entry + = load_bias;
Elf_bss ++ = load_bias;
Elf_brk + = load_bias;
Start_code + = load_bias;
End_code + = load_bias;
Start_data + = load_bias;
End_data + = load_bias;
/* Use set_brk to adjust the BSS segment size */
Retval = set_brk (elf_bss, elf_brk );
If (retval ){
Send_sig (sigkill, current, 0 );
Goto out_free_dentry;
}
If (likely (elf_bss! = Elf_brk) & unlikely (padzero (elf_bss ))){
Send_sig (SIGSEGV, current, 0 );
Retval =-efault;/* nobody gets to see this, ..*/
Goto out_free_dentry;
}
/* The kernel passes control to the dynamic linker.
The dynamic linker checks the program's dependency on the shared library,
And load it as needed, which is completed by load_elf_interp */
If (elf_interpreter ){
Unsigned long uninitialized_var (interp_map_addr );
Elf_entry = load_elf_interp (& loc-> interp_elf_ex,
Interpreter,
& Interp_map_addr,
Load_bias );
If (! Is_err (void *) elf_entry )){
/*
* Load_elf_interp () returns relocation
* Adjustment
*/
Interp_load_addr = elf_entry;
Elf_entry + = loc-> interp_elf_ex.e_entry;
}
If (bad_addr (elf_entry )){
Force_sig (SIGSEGV, current );
Retval = is_err (void *) elf_entry )?
(INT) elf_entry:-einval;
Goto out_free_dentry;
}
Reloc_func_desc = interp_load_addr;
Allow_write_access (Interpreter );
Fput (Interpreter );
Kfree (elf_interpreter );
} Else {
Elf_entry = loc-> elf_ex.e_entry;
If (bad_addr (elf_entry )){
Force_sig (SIGSEGV, current );
Retval =-einval;
Goto out_free_dentry;
}
}
Kfree (elf_phdata );
Set_binfmt (& elf_format );
# Ifdef arch_has_setup_additional_pages
Retval = arch_setup_additional_pages (bprm ,!! Elf_interpreter );
If (retval <0 ){
Send_sig (sigkill, current, 0 );
Goto out;
}
# Endif/* arch_has_setup_additional_pages */
Install_exec_creds (bprm );
Current-> flags & = ~ Pf_forknoexec;
/* Generate the elf ing table in the memory */
Retval = create_elf_tables (bprm, & loc-> elf_ex,
Load_addr, interp_load_addr );
If (retval <0 ){
Send_sig (sigkill, current, 0 );
Goto out;
}
/* Adjust the memory ing content */
Current-> MM-> end_code = end_code;
Current-> MM-> start_code = start_code;
Current-> MM-> start_data = start_data;
Current-> MM-> end_data = end_data;
Current-> MM-> start_stack = bprm-> P;
# Ifdef arch_randomize_brk
If (current-> flags & pf_randomize) & (randomize_va_space> 1 ))
Current-> MM-> BRK = Current-> MM-> start_brk =
Arch_randomize_brk (current-> mm );
# Endif
If (current-> personality & mmap_page_zero ){
/* Why this, you ask ??? Well svr4 maps page 0 as read-only,
And some applications "depend" upon this behavior.
Since we do not have the power to recompile these, we
Emulate the svr4 behavior. Sigh .*/
Down_write (& Current-> MM-> mmap_sem );
Error = do_mmap (null, 0, page_size, prot_read | prot_exec,
Map_fixed | map_private, 0 );
Up_write (& Current-> MM-> mmap_sem );
}
# Ifdef elf_plat_init
/*
* The ABI may specify that certain registers be set up in special
* Ways (on i386 % edX is the address of a dt_fini function,
* Example. In addition, it may also specify (eg, powerpc64 elf)
* That the e_entry field is the address of the function Descriptor
* For the startup routine, rather than the address of the startup
* Routine itself. This macro performs whatever initialization
* The regs structure is required as well as any relocations to
* Function descriptor entries when executing dynamically links apps.
*/
Elf_plat_init (regs, reloc_func_desc );
# Endif
/* Start to execute the program. At this time, it is already a sub-process */
Start_thread (regs, elf_entry, bprm-> P );
Retval = 0;
Out:
Kfree (LOC );
Out_ret:
Return retval;
/* Error cleanup */
Out_free_dentry:
Allow_write_access (Interpreter );
If (Interpreter)
Fput (Interpreter );
Out_free_interp:
Kfree (elf_interpreter );
Out_free_ph:
Kfree (elf_phdata );
Goto out;
}
For the source code for loading elf in the above kernel, the ELF file loading process can be analyzed as follows:
1) The kernel first reads the ELF File Header, then reads the addresses and identifiers of each segment or segment from various data structures, and then calls MMAP () load the content of the loaded segments to the memory. Read the field tag to identify whether the segment is readable, writable, and executable in the memory. The text segment is program code, read-only and executable, and the data segment is readable and writable.
2) Find the corresponding dynamic linker name from the pt_interp segment and load the dynamic linker. Usually/lib/ld-linux.so.2.
3) The kernel sets some tag pairs in the stack of the new process to indicate operations related to the dynamic linker.
4) The kernel passes control to the dynamic linker.
5) the dynamic linker checks the program's dependence on the shared library and loads it as needed.
6) The dynamic linker relocates the external reference of the program and tells the program the address of the referenced external variable/function. The address is located within the memory range when the shared library is loaded. Dynamic Links also have the latency locating feature, that is, they can be relocated only when the "real" symbol needs to be referenced, which is of great help to improve the program running efficiency.
7) the dynamic linker executes the Code marked as. init In the ELF File to initialize the program running.
8) the dynamic linker transfers the control to the program and starts execution from the entry point (main) defined in the ELF file header. In A. Out format and ELF format, the value of the program entry point exists explicitly, while in coff format, it is implicitly defined by the standard.
9) The program starts execution.
Other related materials:
Loading, parsing, and instance analysis of dynamic ELF File links in Linux on intel
Http://www.ibm.com/developerworks/cn/linux/l-elf/part1/