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

来源:互联网 发布:师洋的淘宝骂人截图 编辑:程序博客网 时间:2024/06/08 20:02

参考《程序员的自我修养》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.outa.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.elfstatic.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.elfThere 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  1Key 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.elfElf file type is EXEC (Executable file)Entry point 0x8048130There are 5 program headers, starting at offset 52Program 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/maps08048000-080c5000 r-xp 00000000 08:06 21898678                           /local/honghaos/c/ch6/static.elf080c5000-080c6000 rw-p 0007c000 08:06 21898678                           /local/honghaos/c/ch6/static.elf080c6000-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.outminibash$/bin/lsa.out  malloc.c  minbash.c  sleep.c  static.elfminibash$lsa.out  malloc.c  minbash.c  sleep.c  static.elfminibash$dateWed Jan 16 07:19:20 CST 2013minibash$exiterror!

系统内核代码,

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) bytes1266 *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 image1366 */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}


原创粉丝点击