• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 分析ELF的加載過程

    發表于:2007-07-04來源:作者:點擊數: 標簽:
    對于可執行文件來說,段的加載位置是固定的,程序段表中如實反映了段的加載地址.對于共享庫來?段的加載位置是浮動的,位置無關的,程序段表反映的是以0作為基準地址的相對加載地址.盡管共享庫的連接是不充分的,為了便于 測試 動態鏈接器, Linux 允許直接加載共享
    對于可執行文件來說,段的加載位置是固定的,程序段表中如實反映了段的加載地址.對于共享庫來?段的加載位置是浮動的,位置無關的,程序段表反映的是以0作為基準地址的相對加載地址.盡管共享庫的連接是不充分的,為了便于測試動態鏈接器,Linux允許直接加載共享庫運行.如果應用程序具有動態鏈接器的描述段,內核在完成程序段加載后,緊接著加載動態鏈接器,并且啟動動態鏈接器的

    ELF的可執行文件與共享庫在結構上非常類似,它們具有一張程序段表,用來描述這些段如何映射到?br>?炭占?
    對于可執行文件來說,段的加載位置是固定的,程序段表中如實反映了段的加載地址.對于共享庫來?br>?段的加載位置是浮動的,位置無關的,程序段表反映的是以0作為基準地址的相對加載地址.盡管共享庫的連接是不充分的,為了便于測試動態鏈接器,Linux允許直接加載共享庫運行.如果應用程序具有動態鏈接器的描述段,內核在完成程序段加載后,緊接著加載動態鏈接器,并且啟動動態鏈接器的?br>肟?

    typedef struct elf32_hdr{
    unsigned char e_ident[EI_NIDENT];
    Elf32_Half e_type; /* ET_EXEC ET_DYN 等 */
    Elf32_Half e_machine;
    Elf32_Word e_version;
    Elf32_Addr e_entry; /* Entry point */
    Elf32_Off e_phoff; 程序段描述表的位置
    Elf32_Off e_shoff; 一般段描述表的位置
    Elf32_Word e_flags;
    Elf32_Half e_ehsize; ELF頭的大小
    Elf32_Half e_phentsize; 程序段描述表單元的大小
    Elf32_Half e_phnum; 程序段描述表單元的個數
    Elf32_Half e_shentsize; 一般段描述表的單元大小
    Elf32_Half e_shnum; 一般段描述表單元的個數
    Elf32_Half e_shstrndx;
    } Elf32_Ehdr;

    typedef struct elf32_phdr{
    Elf32_Word p_type; PT_INTERP,PT_LOAD,PT_DYNAMIC等
    Elf32_Off p_offset; 該程序段在ELF文件中的位置
    Elf32_Addr p_vaddr; 該程序段被映射到進程的虛擬地址
    Elf32_Addr p_paddr;
    Elf32_Word p_filesz; 該程序段的文件尺寸
    Elf32_Word p_memsz; 該程序段的內存尺寸
    Elf32_Word p_flags; 該程序段的映射屬性
    Elf32_Word p_align;
    } Elf32_Phdr;

    struct linux_binprm{
    char buf[BINPRM_BUF_SIZE]; 預先讀入的ELF文件頭
    struct page *page[MAX_ARG_PAGES];
    unsigned long p; /* current top of mem */
    int sh_bang;
    struct file * file;
    int e_uid, e_gid;
    kernel_cap_t cap_inheritable, cap_permitted, cap_effective;
    int argc, envc;
    char * filename; /* Name of binary */
    unsigned long loader, exec;
    };
    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;
    int load_addr_set = 0;
    char * elf_interpreter = NULL;
    unsigned int interpreter_type = INTERPRETER_NONE;
    unsigned char ibcs2_interpreter = 0;
    mm_segment_t old_fs;
    unsigned long error;
    struct elf_phdr * elf_ppnt, *elf_phdata;
    unsigned long elf_bss, k, elf_brk;
    int elf_exec_fileno;
    int retval, size, i;
    unsigned long elf_entry, interp_load_addr = 0;
    unsigned long start_code, end_code, start_data, end_data;
    struct elfhdr elf_ex;
    struct elfhdr interp_elf_ex;
    struct exec interp_ex;
    char passed_fileno[6];

    /* Get the exec-header */
    elf_ex = *((struct elfhdr *) bprm->buf);

    retval = -ENOEXEC;
    /* First of all, some simple consistency checks */
    if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
    goto out; 文件頭標記是否匹配

    if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
    goto out; 文件類型是否為可執行文件或共享庫
    if (!elf_check_arch(&elf_ex))
    goto out;
    if (!bprm->file->f_op||!bprm->file->f_op->mmap)
    goto out; 所在的文件系統是否具有文件映射功能

    /* Now read in all of the header information */

    retval = -ENOMEM;
    size = elf_ex.e_phentsize * elf_ex.e_phnum; 求程序段表總長度
    if (size > 65536)
    goto out;
    elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL); 分配程序段表空間
    if (!elf_phdata)
    goto out;

    ; 讀入程序段表
    retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
    if (retval < 0)
    goto out_free_ph;

    retval = get_unused_fd(); 取可用進程文件表的自由槽位
    if (retval < 0)
    goto out_free_ph;
    get_file(bprm->file);
    fd_install(elf_exec_fileno = retval, bprm->file); 將打開的文件安裝到進程文件表

    elf_ppnt = elf_phdata; 指向程序段表
    elf_bss = 0; bss段的起始地址
    elf_brk = 0; bss段的終止地址

    start_code = ~0UL; 代碼段的開始
    end_code = 0; 代碼段的終止
    start_data = 0; 數據段的開始
    end_data = 0; 數據段的終止
    ; 掃描ELF程序段表,搜尋動態鏈接器定義
    for (i = 0; i < elf_ex.e_phnum; i++) {
    if (elf_ppnt->p_type == PT_INTERP) {
    retval = -EINVAL;
    if (elf_interpreter)
    goto out_free_dentry; 如果包含多個動態鏈接器描述項

    /* This is the program interpreter used for
    * shared libraries - for now assume that this
    * is an a.out format binary
    */

    retval = -ENOMEM; 為動態鏈接器名稱字符串分配空間
    elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
    GFP_KERNEL);
    if (!elf_interpreter)
    goto out_free_file;
    ; 將動態鏈接器的文件名讀入內存
    retval = kernel_read(bprm->file, elf_ppnt->p_offset,
    elf_interpreter,
    elf_ppnt->p_filesz);
    if (retval < 0)
    goto out_free_interp;
    /* If the program interpreter is one of these two,
    * then assume an iBCS2 image. Otherwise assume
    * a native linux image.
    */
    if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
    strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0)
    ibcs2_interpreter = 1; 說明應用程序是IBCS2仿真代碼

    interpreter = open_exec(elf_interpreter); 打開動態鏈接器文件
    retval = PTR_ERR(interpreter);
    if (IS_ERR(interpreter))
    goto out_free_interp;
    retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
    ; 讀入動態鏈接器文件頭
    if (retval < 0)
    goto out_free_dentry;

    /* Get the exec headers */
    interp_ex = *((struct exec *) bprm->buf); 假定為aout格式的文件頭結構
    interp_elf_ex = *((struct elfhdr *) bprm->buf); 假定為ELF文件頭結構
    }
    elf_ppnt++; 下一片段目錄項
    }

    /* Some simple consistency checks for the interpreter */
    if (elf_interpreter) {
    ; 如果定義了動態鏈接器,分析其格式類型
    interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;

    /* Now figure out which format our binary is */
    if ((N_MAGIC(interp_ex) != OMAGIC) &&
    (N_MAGIC(interp_ex) != ZMAGIC) &&
    (N_MAGIC(interp_ex) != QMAGIC))
    interpreter_type = INTERPRETER_ELF; 如果不是AOUT標識

    if (memcmp(interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
    interpreter_type &= ~INTERPRETER_ELF; 如果沒有ELF標識

    retval = -ELIBBAD;
    if (!interpreter_type) 不能識別動態鏈接器類型
    goto out_free_dentry;

    /* Make sure only one type was selected */
    if ((interpreter_type & INTERPRETER_ELF) &&
    interpreter_type != INTERPRETER_ELF) {
    printk(KERN_WARNING "ELF: Ambiguous type, using ELF\n");
    interpreter_type = INTERPRETER_ELF;
    }
    }

    /* OK, we are done with that, now set up the arg stuff,
    and then start this sucker up */

    if (!bprm->sh_bang) {
    char * passed_p;

    if (interpreter_type == INTERPRETER_AOUT) {
    sprintf(passed_fileno, "%d", elf_exec_fileno);
    passed_p = passed_fileno;

    if (elf_interpreter) {
    retval = copy_strings_kernel(1,&passed_p,bprm);
    ; 將程序的文件描述符壓入參數堆棧,準備傳遞給aout格式的動態鏈接器
    if (retval)
    goto out_free_dentry;
    bprm->argc++; bprm->page[]中參數的數目
    }
    }
    }

    /* Flush all traces of the currently running executable */
    retval = flush_old_exec(bprm);
    if (retval)
    goto out_free_dentry;

    /* OK, This is the point of no return */
    current->mm->start_data = 0;
    current->mm->end_data = 0;
    current->mm->end_code = 0;
    current->mm->mmap = NULL;
    current->flags &= ~PF_FORKNOEXEC;
    elf_entry = (unsigned long) elf_ex.e_entry; 應用程序的入口地址

    /* Do this immediately, since STACK_TOP as used in setup_arg_pages
    may depend on the personality. */
    SET_PERSONALITY(elf_ex, ibcs2_interpreter);
    ; 如果是ibcs2_interpreter非0,置PER_SVR4代碼個性,否則置PER_LINUX代碼個性

    /* Do this so that we can load the interpreter, if need be. We will
    change some of these later */
    current->mm->rss = 0;
    setup_arg_pages(bprm); /* XXX: check error */
    ; 建立描述堆棧參數頁的初始虛存范圍結構
    current->mm->start_stack = bprm->p;

    /* Try and get dynamic programs out of the way of the 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. */

    load_bias = ELF_PAGESTART(elf_ex.e_type==ET_DYN ? ELF_ET_DYN_BASE : 0);
    ; 如果需要加載的是共享庫,則設置共享庫的加載偏置為ELF_ET_DYN_BASE
    /* Now we do a little grungy work by mmaping the ELF image into
    the correct location in memory. At this point, we assume that
    the image should be loaded at fixed address, not at a variable
    address. */

    old_fs = get_fs();
    set_fs(get_ds());
    ; 再次掃描程序段描述表,映射其中的程序段到進程空間
    for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
    int elf_prot = 0, elf_flags;
    unsigned long vaddr;

    if (elf_ppnt->p_type != PT_LOAD)
    continue; 如果程序段不可加載

    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;
    ; 根據程序段描述設置相應的mmap參數

    vaddr = elf_ppnt->p_vaddr; 段起始地址
    if (elf_ex.e_type == ET_EXEC || load_addr_set) {
    ; 對于可執行程序,使用固定映射
    elf_flags |= MAP_FIXED;
    }

    error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
    ; 將該程序段[eppnt->p_offset,eppnt->p_filesz]映射到虛存(load_bias+vaddr)開始的區域

    if (!load_addr_set) {
    load_addr_set = 1;
    load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
    ; 求出該ELF文件在用戶虛存中的起始地址
    if (elf_ex.e_type == ET_DYN) {
    load_bias += error -
    ELF_PAGESTART(load_bias + vaddr);
    load_addr += error;
    }
    }

    k = elf_ppnt->p_vaddr;
    if (k < start_code) start_code = k; 取最小的段地址作為代碼段起始
    if (start_data < k) start_data = k; 取最大的段地址作為數據段起始

    k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; 這時k指向文件段尾

    if (k > elf_bss)
    elf_bss = k; 取最大文件段尾作為BSS段起始
    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; 這時k指向內存段尾
    if (k > elf_brk)
    elf_brk = k; 取最大的內存段尾作為BSS段的終止
    }
    set_fs(old_fs);
    elf_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;

    if (elf_interpreter) {
    if (interpreter_type == INTERPRETER_AOUT)
    elf_entry = load_aout_interp(&interp_ex,
    interpreter);
    else
    elf_entry = load_elf_interp(&interp_elf_ex, 動態鏈接器的文件頭
    interpreter, 動態鏈接器打開的文件結構
    &interp_load_addr); 輸出鏈接器的加載地址
    ; 可執行程序的入口變為動態鏈接器的入口
    allow_write_access(interpreter);
    fput(interpreter);
    kfree(elf_interpreter);

    if (elf_entry == ~0UL) {
    printk(KERN_ERR "Unable to load interpreter\n");
    kfree(elf_phdata);
    send_sig(SIGSEGV, current, 0);
    return 0;
    }
    }

    kfree(elf_phdata); 釋放程序段表

    if (interpreter_type != INTERPRETER_AOUT)
    sys_close(elf_exec_fileno);

    set_binfmt(&elf_format); 增加ELF內核模塊的引用計數

    compute_creds(bprm);
    current->flags &= ~PF_FORKNOEXEC;
    bprm->p = (unsigned long) 建立入口函數參數表
    create_elf_tables((char *)bprm->p,
    bprm->argc,
    bprm->envc,
    (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL),
    load_addr, load_bias,
    interp_load_addr,
    (interpreter_type == INTERPRETER_AOUT ? 0 : 1));
    /* N.B. passed_fileno might not be initialized? */
    if (interpreter_type == INTERPRETER_AOUT)
    current->mm->arg_start += strlen(passed_fileno) + 1;
    current->mm->start_brk = current->mm->brk = elf_brk;
    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;

    /* Calling set_brk effectively mmaps the pages that we need
    * for the bss and break sections
    */
    set_brk(elf_bss, elf_brk); 建立bss的虛存映射,elf_bss是bss的開始,elf_brk是bss的結束

    padzero(elf_bss); 如果bss不起始于頁連界上,說明與data段有重疊,則將該頁bss區域清0

    #if 0
    printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
    printk("(end_code) %lx\n" , (long) current->mm->end_code);
    printk("(start_code) %lx\n" , (long) current->mm->start_code);
    printk("(start_data) %lx\n" , (long) current->mm->start_data);
    printk("(end_data) %lx\n" , (long) current->mm->end_data);
    printk("(start_stack) %lx\n" , (long) current->mm->start_stack);
    printk("(brk) %lx\n" , (long) current->mm->brk);
    #endif

    if ( current->personality == PER_SVR4 )
    {
    /* 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. */
    /* N.B. Shouldn't the size here be PAGE_SIZE?? */
    down(¤t->mm->mmap_sem);
    error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
    MAP_FIXED | MAP_PRIVATE, 0);
    up(¤t->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 T_FINI function, for
    * example. This macro performs whatever initialization to
    * the regs structure is required.
    */
    ELF_PLAT_INIT(regs);
    #endif

    start_thread(regs, elf_entry, bprm->p); 將返回的eip設為elf_entry,esp設為bprm->p
    if (current->ptrace & PT_PTRACED)
    send_sig(SIGTRAP, current, 0); 如果進程處于跟蹤狀態,則生成SIGTRAP信號
    retval = 0;
    out:
    return retval;

    /* error cleanup */
    out_free_dentry:
    allow_write_access(interpreter);
    fput(interpreter);
    out_free_interp:
    if (elf_interpreter)
    kfree(elf_interpreter);
    out_free_file:
    sys_close(elf_exec_fileno);
    out_free_ph:
    kfree(elf_phdata);
    goto out;
    }
    static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
    struct file * interpreter,
    unsigned long *interp_load_addr)
    {
    struct elf_phdr *elf_phdata;
    struct elf_phdr *eppnt;
    unsigned long load_addr = 0;
    int load_addr_set = 0;
    unsigned long last_bss = 0, elf_bss = 0;
    unsigned long error = ~0UL;
    int retval, i, size;

    /* First of all, some simple consistency checks */
    if (interp_elf_ex->e_type != ET_EXEC &&
    interp_elf_ex->e_type != ET_DYN)
    goto out;
    if (!elf_check_arch(interp_elf_ex))
    goto out;
    if (!interpreter->f_op || !interpreter->f_op->mmap)
    goto out;

    /*
    * If the size of this structure has changed, then punt, since
    * we will be doing the wrong thing.
    */
    if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
    goto out;

    /* Now read in all of the header information */

    size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
    if (size > ELF_MIN_ALIGN)
    goto out; 如果動態鏈接器的程序段表的尺寸大于1個頁面
    elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
    if (!elf_phdata)
    goto out;

    retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);
    error = retval;
    if (retval < 0)
    goto out_close;

    eppnt = elf_phdata;
    for (i=0; ie_phnum; i++, eppnt++) {
    if (eppnt->p_type == PT_LOAD) {
    int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
    int elf_prot = 0;
    unsigned long vaddr = 0;
    unsigned long k, map_addr;

    if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
    if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
    if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
    vaddr = eppnt->p_vaddr;
    if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
    elf_type |= MAP_FIXED;

    map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);

    if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
    load_addr = map_addr - ELF_PAGESTART(vaddr);
    load_addr_set = 1;
    }

    /*
    * Find the end of the file mapping for this phdr, and keep
    * track of the largest address we see for this.
    */
    k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
    if (k > elf_bss)
    elf_bss = k;

    /*
    * Do the same thing for the memory mapping - between
    * elf_bss and last_bss is the bss section.
    */
    k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
    if (k > last_bss)
    last_bss = k;
    }
    }

    /* Now use mmap to map the library into memory. */

    /*
    * Now fill out the bss section. First pad the last page up
    * to the page boundary, and then perform a mmap to make sure
    * that there are zero-mapped pages up to and including the
    * last bss page.
    */
    padzero(elf_bss);
    elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far*/

    /* Map the last of the bss segment */
    if (last_bss > elf_bss)
    do_brk(elf_bss, last_bss - elf_bss);

    *interp_load_addr = load_addr;
    error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;

    out_close:
    kfree(elf_phdata);
    out:
    return error;
    }
    static inline unsigned long
    elf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, inttype)
    {
    unsigned long map_addr;

    down(¤t->mm->mmap_sem);
    map_addr = do_mmap(filep, ELF_PAGESTART(addr),
    eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type,
    eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));

    up(¤t->mm->mmap_sem);
    return(map_addr);
    }
    void set_binfmt(struct linux_binfmt *new)
    {
    struct linux_binfmt *old = current->binfmt;
    if (new && new->module)
    __MOD_INC_USE_COUNT(new->module);
    current->binfmt = new;
    if (old && old->module)
    __MOD_DEC_USE_COUNT(old->module);
    }
    static void set_brk(unsigned long start, unsigned long end)
    {
    start = ELF_PAGEALIGN(start);
    end = ELF_PAGEALIGN(end);
    if (end <= start)
    return;
    do_brk(start, end - start);
    }
    static void padzero(unsigned long elf_bss)
    {
    unsigned long nbyte;

    nbyte = ELF_PAGEOFFSET(elf_bss);
    if (nbyte) {
    nbyte = ELF_MIN_ALIGN - nbyte;
    clear_user((void *) elf_bss, nbyte);
    }
    }

    原文轉自:http://www.kjueaiud.com

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>