您的位置:首页 > 职场人生

程序员的自我修养 ch6 可执行文件的装载与进程

2013-02-03 14:40 239 查看
参考《程序员的自我修养》ch6

1. MMU

MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权。
在ELF中把这些属性相似的,又连在一起的段叫做一个“segment”,而系统正是按照“segment”而不是“section”来映射可执行文件的。

>> cat sleep.c
#include <stdlib.h>

int main()
{
while(1)
{
sleep(1000);
}
}

>> gcc -m32 sleep.c
>> gcc -m32 -static sleep.c -o static.elf
>> file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.8, dynamically linked (uses shared libs), not stripped
>> file static.elf
static.elf: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.8, statically linked, not stripped
>> ldd static.elf
not a dynamic executable
>> ldd a.out
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib32/libc.so.6 (0xf7dac000)
/lib/ld-linux.so.2 (0xf7f0e000)
>> readelf -S static.elf
There are 33 section headers, starting at offset 0x80014:

Section Headers:
[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
[ 0]                   NULL            00000000 000000 000000 00      0   0  0
[ 1] .note.ABI-tag     NOTE            080480d4 0000d4 000020 00   A  0   0  4
[ 2] .init             PROGBITS        080480f4 0000f4 000030 00  AX  0   0  4
[ 3] .text             PROGBITS        08048130 000130 05f448 00  AX  0   0 16
[ 4] __libc_freeres_fn PROGBITS        080a7580 05f580 001022 00  AX  0   0 16
[ 5] .fini             PROGBITS        080a85a4 0605a4 00001c 00  AX  0   0  4
[ 6] .rodata           PROGBITS        080a85c0 0605c0 017150 00   A  0   0 32
[ 7] __libc_subfreeres PROGBITS        080bf710 077710 00002c 00   A  0   0  4
[ 8] __libc_atexit     PROGBITS        080bf73c 07773c 000004 00   A  0   0  4
[ 9] .eh_frame         PROGBITS        080bf740 077740 004bc8 00   A  0   0  4
[10] .gcc_except_table PROGBITS        080c4308 07c308 0000fd 00   A  0   0  1
[11] .tdata            PROGBITS        080c5408 07c408 000010 00 WAT  0   0  4
[12] .tbss             NOBITS          080c5418 07c418 000018 00 WAT  0   0  4
[13] .ctors            PROGBITS        080c5418 07c418 000008 00  WA  0   0  4
[14] .dtors            PROGBITS        080c5420 07c420 00000c 00  WA  0   0  4
[15] .jcr              PROGBITS        080c542c 07c42c 000004 00  WA  0   0  4
[16] .data.rel.ro      PROGBITS        080c5430 07c430 000034 00  WA  0   0  4
[17] .got              PROGBITS        080c5464 07c464 000008 04  WA  0   0  4
[18] .got.plt          PROGBITS        080c546c 07c46c 00000c 04  WA  0   0  4
[19] .data             PROGBITS        080c5480 07c480 000714 00  WA  0   0 32
[20] .bss              NOBITS          080c5ba0 07cb94 0019d8 00  WA  0   0 32
[21] __libc_freeres_pt NOBITS          080c7578 07cb94 000014 00  WA  0   0  4
[22] .comment          PROGBITS        00000000 07cb94 002e6e 00      0   0  1
[23] .debug_aranges    PROGBITS        00000000 07fa08 000050 00      0   0  8
[24] .debug_pubnames   PROGBITS        00000000 07fa58 000025 00      0   0  1
[25] .debug_info       PROGBITS        00000000 07fa7d 0001a9 00      0   0  1
[26] .debug_abbrev     PROGBITS        00000000 07fc26 00006f 00      0   0  1
[27] .debug_line       PROGBITS        00000000 07fc95 00012b 00      0   0  1
[28] .debug_str        PROGBITS        00000000 07fdc0 0000bb 01  MS  0   0  1
[29] .debug_ranges     PROGBITS        00000000 07fe80 000040 00      0   0  8
[30] .shstrtab         STRTAB          00000000 07fec0 000152 00      0   0  1
[31] .symtab           SYMTAB          00000000 08053c 007550 10     32 799  4
[32] .strtab           STRTAB          00000000 087a8c 006c1c 00      0   0  1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)

>> readelf -l static.elf

Elf file type is EXEC (Executable file)
Entry point 0x8048130
There are 5 program headers, starting at offset 52

Program Headers:
Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
LOAD           0x000000 0x08048000 0x08048000 0x7c405 0x7c405 R E 0x1000
LOAD           0x07c408 0x080c5408 0x080c5408 0x0078c 0x02184 RW  0x1000
NOTE           0x0000d4 0x080480d4 0x080480d4 0x00020 0x00020 R   0x4
TLS            0x07c408 0x080c5408 0x080c5408 0x00010 0x00028 R   0x4
GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

Section to Segment mapping:
Segment Sections...
00     .note.ABI-tag .init .text __libc_freeres_fn .fini .rodata __libc_subfreeres __libc_atexit .eh_frame .gcc_except_table
01     .tdata .ctors .dtors .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs
02     .note.ABI-tag
03     .tdata .tbss
04

数据结构定义,

/* Program segment header. */

typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;


2. 堆和栈

VMA除了以上功能外,还被操作系统用来对进程的地址空间进行管理。 进程执行时的栈(Stack)和堆(Heap)在虚拟地址空间中也是以VMA的形式存在的。 可以通过查看“cat /proc/进程号/maps”来查看进程额虚拟地址空间分布。
>> ./static.elf &
[1] 23099

>> cat /proc/23099/maps
08048000-080c5000 r-xp 00000000 08:06 21898678 /local/honghaos/c/ch6/static.elf
080c5000-080c6000 rw-p 0007c000 08:06 21898678 /local/honghaos/c/ch6/static.elf
080c6000-080ea000 rw-p 080c6000 00:00 0 [heap]
fff38000-fff4d000 rw-p 7ffffffea000 00:00 0 [stack]
ffffe000-fffff000 r-xp ffffe000 00:00 0 [vdso]

通过上述命令,我们可以看到有两个匿名虚拟内存区域(Anonymous Virtual Memory Area),它们分别是[heap]和[stack]。 另外有一个特殊的的VMA叫做“vdso”,它的地址已经位于内核空间了(大于0xC0000000),实际上它是一个内核模块, 可以通过访问这个VMA来跟内核进行一些通信。

在此再小结下进程虚拟地址空间的概念:操作系统通过给进程空间划分一个个VMA来管理进程的虚拟空间。 基本原则是将相同权限属性的、有相同映像文件的映射成一个VMA。 一个进程基本上可以分为如下几种VMA区域:

a. 代码VMA,权限只读、可执行;有映像文件。

b. 数据VMA,权限可读写、可执行;有映像文件。

c. 堆VMA,权限可读写、可执行;无映像文件,匿名,可向上扩展。

d. 栈VMA,权限可读写、不可执行;无映像文件,匿名,可向下扩展。

进程刚开始启动时,需要一些进程运行的环境,最基本的就是系统环境变量和进程的运行参数。 很常见的一种做法就是操作系统在进程启动前将这些信息提前保存到进程的虚拟空间的栈中(也即是VMA中的Stack VMA)。 然后再在main函数中读取这些参数,如argc和argv。

3. Linux内核装载ELF过程简介

在bash下执行一个程序时,Linux是怎样装载这个ELF文件并执行的呢?

首先bash调用fork()系统调用创建一个新的进程,然后新的进程调用execve()系统调用执行指定的ELF文件。 bash进程继续返回等待新进程执行结束,然后重新等待用户输入命令。
>> cat minbash.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
char buf[1024] = {0};
pid_t pid;

while(1)
{
printf("minibash$");
scanf("%s", buf);
pid = fork();
if(pid == 0)
{
if(execlp(buf, 0) < 0)
{
printf("error!\n");
}
}
else if(pid > 0)
{
int status;
waitpid(pid, &status, 0);
}
else
{
printf("fork error %d\n", pid);
}
}
return 0;
}

>> ./a.out
minibash$/bin/ls
a.out malloc.c minbash.c sleep.c static.elf
minibash$ls
a.out malloc.c minbash.c sleep.c static.elf
minibash$date
Wed Jan 16 07:19:20 CST 2013
minibash$exit
error!

系统内核代码,
arch/m32r/kernel/process.c, line 292 

289/*
290 * sys_execve() executes a new program.
291 */
292asmlinkage int sys_execve(const char __user *ufilename,
293 const char __user *const __user *uargv,
294 const char __user *const __user *uenvp,
295 unsigned long r3, unsigned long r4, unsigned long r5,
296 unsigned long r6, struct pt_regs regs)
297{
298 int error;
299 struct filename *filename;
300
301 filename = getname(ufilename);
302 error = PTR_ERR(filename);
303 if (IS_ERR(filename))
304 goto out;
305
306 error = do_execve(filename->name, uargv, uenvp, ®s);
307 putname(filename);
308out:
309 return error;
310}

linux/fs/exec.c
1263/*
1264 * Fill the binprm structure from the inode.
1265 * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
1266 *
1267 * This may be called multiple times for binary chains (scripts for example).
1268 */
1269int prepare_binprm(struct linux_binprm *bprm)

脚本程序的开始部分 “#!”也是magic number
1364/*
1365 * cycle the list of binary formats handler, until one recognizes the image
1366 */
1367int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
search_binary_handler()会通过判断文件头部的魔数确定文件的格式,并且调用相应的装载处理过程。例如,
3.1 加载elf文件
fs/binfmt_elf.c, line 561 

561static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
562{
563 struct file *interpreter = NULL; /* to shut gcc up */
564 unsigned long load_addr = 0, load_bias = 0;
565 int load_addr_set = 0;
566 char * elf_interpreter = NULL;
567 unsigned long error;
568 struct elf_phdr *elf_ppnt, *elf_phdata;
569 unsigned long elf_bss, elf_brk;
570 int retval, i;
571 unsigned int size;
572 unsigned long elf_entry;
573 unsigned long interp_load_addr = 0;
574 unsigned long start_code, end_code, start_data, end_data;
575 unsigned long reloc_func_desc __maybe_unused = 0;
576 int executable_stack = EXSTACK_DEFAULT;
577 unsigned long def_flags = 0;
578 struct {
579 struct elfhdr elf_ex;
580 struct elfhdr interp_elf_ex;
581 } *loc;
582
583 loc = kmalloc(sizeof(*loc), GFP_KERNEL);
584 if (!loc) {
585 retval = -ENOMEM;
586 goto out_ret;
587 }
588
589 /* Get the exec-header */
590 loc->elf_ex = *((struct elfhdr *)bprm->buf);
591
592 retval = -ENOEXEC;
593 /* First of all, some simple consistency checks */
594 if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
595 goto out;
596
597 if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
598 goto out;
599 if (!elf_check_arch(&loc->elf_ex))
600 goto out;
601 if (!bprm->file->f_op || !bprm->file->f_op->mmap)
602 goto out;
603
604 /* Now read in all of the header information */
605 if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
606 goto out;
607 if (loc->elf_ex.e_phnum < 1 ||
608 loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
609 goto out;
610 size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
611 retval = -ENOMEM;
612 elf_phdata = kmalloc(size, GFP_KERNEL);
613 if (!elf_phdata)
614 goto out;
615
616 retval = kernel_read(bprm->file, loc->elf_ex.e_phoff,
617 (char *)elf_phdata, size);
618 if (retval != size) {
619 if (retval >= 0)
620 retval = -EIO;
621 goto out_free_ph;
622 }
623
624 elf_ppnt = elf_phdata;
625 elf_bss = 0;
626 elf_brk = 0;
627
628 start_code = ~0UL;
629 end_code = 0;
630 start_data = 0;
631 end_data = 0;
632
633 for (i = 0; i < loc->elf_ex.e_phnum; i++) {
634 if (elf_ppnt->p_type == PT_INTERP) {
635 /* This is the program interpreter used for
636 * shared libraries - for now assume that this
637 * is an a.out format binary
638 */
639 retval = -ENOEXEC;
640 if (elf_ppnt->p_filesz > PATH_MAX ||
641 elf_ppnt->p_filesz < 2)
642 goto out_free_ph;
643
644 retval = -ENOMEM;
645 elf_interpreter = kmalloc(elf_ppnt->p_filesz,
646 GFP_KERNEL);
647 if (!elf_interpreter)
648 goto out_free_ph;
649
650 retval = kernel_read(bprm->file, elf_ppnt->p_offset,
651 elf_interpreter,
652 elf_ppnt->p_filesz);
653 if (retval != elf_ppnt->p_filesz) {
654 if (retval >= 0)
655 retval = -EIO;
656 goto out_free_interp;
657 }
658 /* make sure path is NULL terminated */
659 retval = -ENOEXEC;
660 if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
661 goto out_free_interp;
662
663 interpreter = open_exec(elf_interpreter);
664 retval = PTR_ERR(interpreter);
665 if (IS_ERR(interpreter))
666 goto out_free_interp;
667
668 /*
669 * If the binary is not readable then enforce
670 * mm->dumpable = 0 regardless of the interpreter's
671 * permissions.
672 */
673 would_dump(bprm, interpreter);
674
675 retval = kernel_read(interpreter, 0, bprm->buf,
676 BINPRM_BUF_SIZE);
677 if (retval != BINPRM_BUF_SIZE) {
678 if (retval >= 0)
679 retval = -EIO;
680 goto out_free_dentry;
681 }
682
683 /* Get the exec headers */
684 loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
685 break;
686 }
687 elf_ppnt++;
688 }
689
690 elf_ppnt = elf_phdata;
691 for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
692 if (elf_ppnt->p_type == PT_GNU_STACK) {
693 if (elf_ppnt->p_flags & PF_X)
694 executable_stack = EXSTACK_ENABLE_X;
695 else
696 executable_stack = EXSTACK_DISABLE_X;
697 break;
698 }
699
700 /* Some simple consistency checks for the interpreter */
701 if (elf_interpreter) {
702 retval = -ELIBBAD;
703 /* Not an ELF interpreter */
704 if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
705 goto out_free_dentry;
706 /* Verify the interpreter has a valid arch */
707 if (!elf_check_arch(&loc->interp_elf_ex))
708 goto out_free_dentry;
709 }
710
711 /* Flush all traces of the currently running executable */
712 retval = flush_old_exec(bprm);
713 if (retval)
714 goto out_free_dentry;
715
716 /* OK, This is the point of no return */
717 current->mm->def_flags = def_flags;
718
719 /* Do this immediately, since STACK_TOP as used in setup_arg_pages
720 may depend on the personality. */
721 SET_PERSONALITY(loc->elf_ex);
722 if (elf_read_implies_exec(loc->elf_ex, executable_stack))
723 current->personality |= READ_IMPLIES_EXEC;
724
725 if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
726 current->flags |= PF_RANDOMIZE;
727
728 setup_new_exec(bprm);
729
730 /* Do this so that we can load the interpreter, if need be. We will
731 change some of these later */
732 current->mm->free_area_cache = current->mm->mmap_base;
733 current->mm->cached_hole_size = 0;
734 retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
735 executable_stack);
736 if (retval < 0) {
737 send_sig(SIGKILL, current, 0);
738 goto out_free_dentry;
739 }
740
741 current->mm->start_stack = bprm->p;
742
743 /* Now we do a little grungy work by mmapping the ELF image into
744 the correct location in memory. */
745 for(i = 0, elf_ppnt = elf_phdata;
746 i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
747 int elf_prot = 0, elf_flags;
748 unsigned long k, vaddr;
749
750 if (elf_ppnt->p_type != PT_LOAD)
751 continue;
752
753 if (unlikely (elf_brk > elf_bss)) {
754 unsigned long nbyte;
755
756 /* There was a PT_LOAD segment with p_memsz > p_filesz
757 before this one. Map anonymous pages, if needed,
758 and clear the area. */
759 retval = set_brk(elf_bss + load_bias,
760 elf_brk + load_bias);
761 if (retval) {
762 send_sig(SIGKILL, current, 0);
763 goto out_free_dentry;
764 }
765 nbyte = ELF_PAGEOFFSET(elf_bss);
766 if (nbyte) {
767 nbyte = ELF_MIN_ALIGN - nbyte;
768 if (nbyte > elf_brk - elf_bss)
769 nbyte = elf_brk - elf_bss;
770 if (clear_user((void __user *)elf_bss +
771 load_bias, nbyte)) {
772 /*
773 * This bss-zeroing can fail if the ELF
774 * file specifies odd protections. So
775 * we don't check the return value
776 */
777 }
778 }
779 }
780
781 if (elf_ppnt->p_flags & PF_R)
782 elf_prot |= PROT_READ;
783 if (elf_ppnt->p_flags & PF_W)
784 elf_prot |= PROT_WRITE;
785 if (elf_ppnt->p_flags & PF_X)
786 elf_prot |= PROT_EXEC;
787
788 elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
789
790 vaddr = elf_ppnt->p_vaddr;
791 if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
792 elf_flags |= MAP_FIXED;
793 } else if (loc->elf_ex.e_type == ET_DYN) {
794 /* Try and get dynamic programs out of the way of the
795 * default mmap base, as well as whatever program they
796 * might try to exec. This is because the brk will
797 * follow the loader, and is not movable. */
798#ifdef CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE
799 /* Memory randomization might have been switched off
800 * in runtime via sysctl.
801 * If that is the case, retain the original non-zero
802 * load_bias value in order to establish proper
803 * non-randomized mappings.
804 */
805 if (current->flags & PF_RANDOMIZE)
806 load_bias = 0;
807 else
808 load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
809#else
810 load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
811#endif
812 }
813
814 error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
815 elf_prot, elf_flags, 0);
816 if (BAD_ADDR(error)) {
817 send_sig(SIGKILL, current, 0);
818 retval = IS_ERR((void *)error) ?
819 PTR_ERR((void*)error) : -EINVAL;
820 goto out_free_dentry;
821 }
822
823 if (!load_addr_set) {
824 load_addr_set = 1;
825 load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
826 if (loc->elf_ex.e_type == ET_DYN) {
827 load_bias += error -
828 ELF_PAGESTART(load_bias + vaddr);
829 load_addr += load_bias;
830 reloc_func_desc = load_bias;
831 }
832 }
833 k = elf_ppnt->p_vaddr;
834 if (k < start_code)
835 start_code = k;
836 if (start_data < k)
837 start_data = k;
838
839 /*
840 * Check to see if the section's size will overflow the
841 * allowed task size. Note that p_filesz must always be
842 * <= p_memsz so it is only necessary to check p_memsz.
843 */
844 if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
845 elf_ppnt->p_memsz > TASK_SIZE ||
846 TASK_SIZE - elf_ppnt->p_memsz < k) {
847 /* set_brk can never work. Avoid overflows. */
848 send_sig(SIGKILL, current, 0);
849 retval = -EINVAL;
850 goto out_free_dentry;
851 }
852
853 k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
854
855 if (k > elf_bss)
856 elf_bss = k;
857 if ((elf_ppnt->p_flags & PF_X) && end_code < k)
858 end_code = k;
859 if (end_data < k)
860 end_data = k;
861 k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
862 if (k > elf_brk)
863 elf_brk = k;
864 }
865
866 loc->elf_ex.e_entry += load_bias;
867 elf_bss += load_bias;
868 elf_brk += load_bias;
869 start_code += load_bias;
870 end_code += load_bias;
871 start_data += load_bias;
872 end_data += load_bias;
873
874 /* Calling set_brk effectively mmaps the pages that we need
875 * for the bss and break sections. We must do this before
876 * mapping in the interpreter, to make sure it doesn't wind
877 * up getting placed where the bss needs to go.
878 */
879 retval = set_brk(elf_bss, elf_brk);
880 if (retval) {
881 send_sig(SIGKILL, current, 0);
882 goto out_free_dentry;
883 }
884 if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) {
885 send_sig(SIGSEGV, current, 0);
886 retval = -EFAULT; /* Nobody gets to see this, but.. */
887 goto out_free_dentry;
888 }
889
890 if (elf_interpreter) {
891 unsigned long interp_map_addr = 0;
892
893 elf_entry = load_elf_interp(&loc->interp_elf_ex,
894 interpreter,
895 &interp_map_addr,
896 load_bias);
897 if (!IS_ERR((void *)elf_entry)) {
898 /*
899 * load_elf_interp() returns relocation
900 * adjustment
901 */
902 interp_load_addr = elf_entry;
903 elf_entry += loc->interp_elf_ex.e_entry;
904 }
905 if (BAD_ADDR(elf_entry)) {
906 force_sig(SIGSEGV, current);
907 retval = IS_ERR((void *)elf_entry) ?
908 (int)elf_entry : -EINVAL;
909 goto out_free_dentry;
910 }
911 reloc_func_desc = interp_load_addr;
912
913 allow_write_access(interpreter);
914 fput(interpreter);
915 kfree(elf_interpreter);
916 } else {
917 elf_entry = loc->elf_ex.e_entry;
918 if (BAD_ADDR(elf_entry)) {
919 force_sig(SIGSEGV, current);
920 retval = -EINVAL;
921 goto out_free_dentry;
922 }
923 }
924
925 kfree(elf_phdata);
926
927 set_binfmt(&elf_format);
928
929#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
930 retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
931 if (retval < 0) {
932 send_sig(SIGKILL, current, 0);
933 goto out;
934 }
935#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
936
937 install_exec_creds(bprm);
938 retval = create_elf_tables(bprm, &loc->elf_ex,
939 load_addr, interp_load_addr);
940 if (retval < 0) {
941 send_sig(SIGKILL, current, 0);
942 goto out;
943 }
944 /* N.B. passed_fileno might not be initialized? */
945 current->mm->end_code = end_code;
946 current->mm->start_code = start_code;
947 current->mm->start_data = start_data;
948 current->mm->end_data = end_data;
949 current->mm->start_stack = bprm->p;
950
951#ifdef arch_randomize_brk
952 if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
953 current->mm->brk = current->mm->start_brk =
954 arch_randomize_brk(current->mm);
955#ifdef CONFIG_COMPAT_BRK
956 current->brk_randomized = 1;
957#endif
958 }
959#endif
960
961 if (current->personality & MMAP_PAGE_ZERO) {
962 /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
963 and some applications "depend" upon this behavior.
964 Since we do not have the power to recompile these, we
965 emulate the SVr4 behavior. Sigh. */
966 error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
967 MAP_FIXED | MAP_PRIVATE, 0);
968 }
969
970#ifdef ELF_PLAT_INIT
971 /*
972 * The ABI may specify that certain registers be set up in special
973 * ways (on i386 %edx is the address of a DT_FINI function, for
974 * example. In addition, it may also specify (eg, PowerPC64 ELF)
975 * that the e_entry field is the address of the function descriptor
976 * for the startup routine, rather than the address of the startup
977 * routine itself. This macro performs whatever initialization to
978 * the regs structure is required as well as any relocations to the
979 * function descriptor entries when executing dynamically links apps.
980 */
981 ELF_PLAT_INIT(regs, reloc_func_desc);
982#endif
983
984 start_thread(regs, elf_entry, bprm->p);
985 retval = 0;
986out:
987 kfree(loc);
988out_ret:
989 return retval;
990
991 /* error cleanup */
992out_free_dentry:
993 allow_write_access(interpreter);
994 if (interpreter)
995 fput(interpreter);
996out_free_interp:
997 kfree(elf_interpreter);
998out_free_ph:
999 kfree(elf_phdata);
1000 goto out;
1001}

3.2 加载aout文件

fs/binfmt_aout.c, line 204
199/*
200 * These are the functions used to load a.out style executables and shared
201 * libraries. There is no binary dependent code anywhere else.
202 */
203
204static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)

3.3 加载script
fs/binfmt_script.c, line 17

17static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)

最后,在load_elf_binary()中系统调用的返回地址已经被改成ELF程序的入口地址了“start_thread(regs, elf_entry, bprm->p);”。所以当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到了ELF程序的出口地址,程序开始执行。
199void
200start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
201{
202 set_user_gs(regs, 0);
203 regs->fs = 0;
204 regs->ds = __USER_DS;
205 regs->es = __USER_DS;
206 regs->ss = __USER_DS;
207 regs->cs = __USER_CS;
208 regs->ip = new_ip;
209 regs->sp = new_sp;
210 regs->flags = X86_EFLAGS_IF;
211 /*
212 * force it to the iret return path by making it look as if there was
213 * some work pending.
214 */
215 set_thread_flag(TIF_NOTIFY_RESUME);
216}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: