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

参考《程序员的自我修养》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}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值