乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      linux可執(zhí)行文件的加載和運(yùn)行

       看風(fēng)景D人 2014-09-16

          linux可執(zhí)行文件的加載和運(yùn)行之一

          

          三:可執(zhí)行文件的加載和運(yùn)行

          Execve系統(tǒng)調(diào)用可以調(diào)用一個可執(zhí)行文件完全代替當(dāng)前的進(jìn)程,它在libc中的封裝有幾個API:

          int execl(const charp a t* h n a m e, const char a* rg 0, ... /* (char *) 0 */);

          int execv(const charp a t* h n a m e, char *consta rgv [] );

          int execle(const charp a t* h n a m e, const char a* rg 0, ...

          /* (char *)0, char *cones nt v p [] */);

          int execve(const charp a t* h n a m e, char *consta rgv [], char *consten vp [] );

          int execlp(const charf i l e* n a m e, const char a* rg 0, ... /* (char *) 0 */);

          int execvp(const charf i l e* n a m e, char *consta rgv [] );

          我們深入內(nèi)核代碼來研究一下可執(zhí)行文件的加載過程.execve()系統(tǒng)調(diào)用的入口是sys_execve().代碼如下:

          asmlinkage int sys_execve(struct pt_regs regs)

          {

           int error;

           char * filename;

          

           //將用戶空間的第一個參數(shù)(也就是可執(zhí)行文件的路徑)復(fù)制到內(nèi)核

           filename = getname((char __user *) regs.ebx);

           error = PTR_ERR(filename);

           if (IS_ERR(filename))

           goto out;

           error = do_execve(filename,

           (char __user * __user *) regs.ecx,

           (char __user * __user *) regs.edx,

           s);

           if (error == 0) {

           task_lock(current);

           current->ptrace &= ~PT_DTRACE;

           task_unlock(current);

           /* Make sure we don't return using sysenter.. */

           set_thread_flag(TIF_IRET);

           }

           //釋放內(nèi)存

           putname(filename);

          out:

           return error;

          }

          系統(tǒng)調(diào)用的時候,把參數(shù)依次放在:ebx,ecx,edx,esi,edi,ebp寄存器.詳情請參閱本站 Linux中斷處理之系統(tǒng)調(diào)用>>.第一個參數(shù)為可執(zhí)行文件路徑,第二個參數(shù)為參數(shù)的個數(shù),第三個參數(shù)為可執(zhí)行文件對應(yīng)的參數(shù).

          do_execve()是這個系統(tǒng)調(diào)用的核心,它的代碼如下:

          int do_execve(char * filename,

           char __user *__user *argv,

           char __user *__user *envp,

           struct pt_regs * regs)

          {

           //linux_binprm:保存可執(zhí)行文件的一些參數(shù)

           struct linux_binprm *bprm;

           struct file *file;

           unsigned long env_p;

           int retval;

          

           retval = -ENOMEM;

           bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);

           if (!bprm)

           goto out_ret;

          

           //在內(nèi)核中打開這個可執(zhí)行文件

           file = open_exec(filename);

           retval = PTR_ERR(file);

           //如果打開失敗

           if (IS_ERR(file))

           goto out_kfree;

          

           sched_exec();

          

           bprm->file = file;

           bprm->filename = filename;

           bprm->interp = filename;

          

           //bprm初始化,主要是初始化bprm->mm

           retval = bprm_mm_init(bprm);

           if (retval)

           goto out_file;

          

           //計算參數(shù)個數(shù)

           bprm->argc = count(argv, MAX_ARG_STRINGS);

           if ((retval = bprm->argc)

           goto out_mm;

          

           //環(huán)境變量個數(shù)

           bprm->envc = count(envp, MAX_ARG_STRINGS);

           if ((retval = bprm->envc)

           goto out_mm;

          

           retval = security_bprm_alloc(bprm);

           if (retval)

           goto out;

          

           //把要加載文件的前128 讀入bprm->buf

           retval = prepare_binprm(bprm);

           if (retval

           goto out;

           //copy第一個參數(shù)filename

           retval = copy_strings_kernel(1, &bprm->filename, bprm);

           if (retval

           goto out;

           //bprm->exec:參數(shù)的起始地址(從上往下方向)

           bprm->exec = bprm->p;

           //copy環(huán)境變量

           retval = copy_strings(bprm->envc, envp, bprm);

           if (retval

           goto out;

           //環(huán)境變量存放的起始地址

           env_p = bprm->p;

           //copy可執(zhí)行文件所帶參數(shù)

           retval = copy_strings(bprm->argc, argv, bprm);

           if (retval

           goto out;

           //環(huán)境變量的長度

           bprm->argv_len = env_p - bprm->p;

          

           //到鏈表中尋找合適的加載模塊

           retval = search_binary_handler(bprm,regs);

           if (retval >= 0) {

           /* execve success */

           free_arg_pages(bprm);

           security_bprm_free(bprm);

           acct_update_integrals(current);

           kfree(bprm);

           return retval;

           }

          

          out:

           free_arg_pages(bprm);

           if (bprm->security)

           security_bprm_free(bprm);

          

          out_mm:

           if (bprm->mm)

           mmput (bprm->mm);

          

          out_file:

           if (bprm->file) {

           allow_write_access(bprm->file);

           fput(bprm->file);

           }

          out_kfree:

           kfree(bprm);

          

          out_ret:

           return retval;

          }

          研究代碼之前,我們先考慮一下進(jìn)程的空間安排結(jié)構(gòu).在本站的中的malloc機(jī)制分析>>曾經(jīng)描述過.我們再次把進(jìn)程的空間結(jié)構(gòu)圖列出,如下如示:

          

          

          用戶棧位于進(jìn)程空間的最高部份.那進(jìn)程初始化時,用戶棧存放的是什么呢?是參數(shù).進(jìn)程在執(zhí)行時會到棧中去取運(yùn)行時所需的參數(shù).這里所謂的參數(shù)包含了可執(zhí)行程序所帶的參數(shù)和環(huán)境變量.例如:在shell上執(zhí)行”echo hello,eric” .echo程序帶有二個參數(shù).argv[0] = “echo”,argv[1] = “hello,eric”即第一個參數(shù)為程序名稱.其后的參數(shù)分別是運(yùn)行進(jìn)程所帶的參數(shù).當(dāng)然,在上面這個例子中沒有列出環(huán)境變量.一般的.在參數(shù)后面都跟了一個NULL.表示參數(shù)已經(jīng)結(jié)束了,在上例中argv[1]后面的一個字節(jié)是NULL.如下圖所示:

          

          

          這樣程序在運(yùn)行的時候就可以方便的確定參數(shù)及環(huán)境變量的個數(shù).

          現(xiàn)在,我們可以分析代碼了.

          bprm_mm_init()是bprm的初始化函數(shù),我們跟蹤進(jìn)去看它是怎么樣初始化的.

          int bprm_mm_init(struct linux_binprm *bprm)

          {

           int err;

           struct mm_struct *mm = NULL;

          

           //分配一個mm

          //mm_alloc我們在進(jìn)程創(chuàng)建的時候已經(jīng)分析過了,值得注意的是,它會調(diào)用mm_init()來為

          //進(jìn)程的用戶空間建立PGD->PMD映射

           bprm->mm = mm = mm_alloc();

           err = -ENOMEM;

           if (!mm)

           goto err;

          

           err = init_new_context(current, mm);

           if (err)

           goto err;

           //初始化bprm->mm

           err = __bprm_mm_init(bprm);

           if (err)

           goto err;

          

           return 0;

          

          err:

           if (mm) {

           bprm->mm = NULL;

           mmdrop(mm);

           }

          

           return err;

          }

          重點是在__bprm_mm_init():

          static int __bprm_mm_init(struct linux_binprm *bprm)

          {

           int err = -ENOMEM;

           struct vm_area_struct *vma = NULL;

           struct mm_struct *mm = bprm->mm;

          

           //分配一個VMA

           bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);

           if (!vma)

           goto err;

          

           down_write(&mm->mmap_sem);

           vma->vm_mm = mm;

          

           //STACK_TOP_MAX:進(jìn)程用戶空間的最高值

           //對應(yīng)進(jìn)程的棧頂

           vma->vm_end = STACK_TOP_MAX;

           vma->vm_start = vma->vm_end - PAGE_SIZE;

          

           vma->vm_flags = VM_STACK_FLAGS;

           vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);

           //將VM插入mm表示的進(jìn)程空間結(jié)構(gòu)

           err = insert_vm_struct(mm, vma);

           if (err) {

           up_write(&mm->mmap_sem);

           goto err;

           }

          

           mm->stack_vm = mm->total_vm = 1;

           up_write(&mm->mmap_sem);

          

           //bprm->p:用戶棧的棧指針

           bprm->p = vma->vm_end - sizeof(void *);

          

           return 0;

          

          err:

           if (vma) {

           bprm->vma = NULL;

           kmem_cache_free(vm_area_cachep, vma);

           }

          

           return err;

          }

          上面的操作看起來比較隱晦,我們把它的操作用下面的圖表示:

          

          

          在這里為bprm->mm的初始化下了這么多功夫是為什么呢?它跟進(jìn)程的mm有什么關(guān)系?不急,繼續(xù)耐著性子看代碼,我們會看到它的用途的.

          繼續(xù)分析do_execve()中所調(diào)用的子函數(shù).

          Count()來用計算可執(zhí)行文件的參數(shù)或者環(huán)境變量的個數(shù).它的代碼如下:

          static int count(char __user * __user * argv, int max)

          {

           int i = 0;

          

           if (argv != NULL) {

           for (;;) {

           char __user * p;

           //在內(nèi)核空間中取argv的值

          

           //取值失敗

           if (get_user(p, argv))

           return -EFAULT;

           //如果為空。說明已經(jīng)取到了NULL。結(jié)束了

           if (!p)

           break;

           argv++;

           //參數(shù)個數(shù)超過了允許的最大值

           if(++i > max)

           return -E2BIG;

           cond_resched();

           }

           }

           return i;

          }

          這個函數(shù)的原理是利用參數(shù)后面是以NULL結(jié)尾的,不懂的請回個頭去看下上面的分析.

          疑問:在取參數(shù)個數(shù)的時候,會進(jìn)行用戶空間到內(nèi)核空間的copy.但是這里僅僅是得知它的個數(shù),在后面的操作中,還會繼續(xù)去取參數(shù)值放到bprm->mm表示的空間中.這里有兩次拷copy.可不可把這兩個過程放在一起.省掉一次從用戶空間到內(nèi)核空間的COPY呢?

          prepare_binprm()會將文件的前128字節(jié)copy到bprm->buf.代碼片段如下所示:

          int prepare_binprm(struct linux_binprm *bprm)

          {

           ……

           ……

           memset(bprm->buf,0,BINPRM_BUF_SIZE);

           //#define BINPRM_BUF_SIZE 128

           return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);

          }

          將具體的參數(shù)COPY到bprm->mm所表示的存儲空間中是由copy_strings()完成的.它的代碼有一點繁鎖.如下示:

          /*

           參數(shù)含義:

           argc:參數(shù)個數(shù)

           argv:參數(shù)數(shù)組

          */

          static int copy_strings(int argc, char __user * __user * argv,

           struct linux_binprm *bprm)

          {

           struct page *kmapped_page = NULL;

           char *kaddr = NULL;

           unsigned long kpos = 0;

           int ret;

          

          

           while (argc-- > 0) {

           char __user *str;

           int len;

           unsigned long pos;

          

           //取數(shù)組相應(yīng)項,將其放至str中

          

           //COPY失敗,或者參數(shù)長度非法

           if (get_user(str, argv+argc) ||

           !(len = strnlen_user(str, MAX_ARG_STRLEN))) {

           ret = -EFAULT;

           goto out;

           }

          

           //判斷參數(shù)長度是否超過允許的最大值

           if (!valid_arg_len(bprm, len)) {

           ret = -E2BIG;

           goto out;

           }

          

           /* We're going to work our way backwords. */

           //當(dāng)前的位置

           pos = bprm->p;

           str += len;

           bprm->p -= len;

          

           while (len > 0) {

           int offset, bytes_to_copy;

          

           offset = pos % PAGE_SIZE;

           if (offset == 0)

           offset = PAGE_SIZE;

          

           bytes_to_copy = offset;

           if (bytes_to_copy > len)

           bytes_to_copy = len;

          

           offset -= bytes_to_copy;

           pos -= bytes_to_copy;

           str -= bytes_to_copy;

           len -= bytes_to_copy;

          

           if (!kmapped_page || kpos != (pos & PAGE_MASK)) {

           struct page *page;

          

           //根據(jù)映射關(guān)系得到pos地址在bprm->mm中所映射的頁面

           page = get_arg_page(bprm, pos, 1);

           if (!page) {

           ret = -E2BIG;

           goto out;

           }

          

           if (kmapped_page) {

           flush_kernel_dcache_page(kmapped_page);

           //斷開臨時映射

           kunmap(kmapped_page);

           //減少引用計數(shù)

           put_arg_page(kmapped_page);

           }

           kmapped_page = page;

           //將臨時映射到內(nèi)核

           kaddr = kmap(kmapped_page);

           kpos = pos & PAGE_MASK;

           flush_arg_page(bprm, kpos, kmapped_page);

           }

           //copy參數(shù)至剛才映射的頁面

           if (copy_from_user(kaddr+offset, str, bytes_to_copy)) {

           ret = -EFAULT;

           goto out;

           }

           }

           }

           ret = 0;

          out:

           if (kmapped_page) {

           flush_kernel_dcache_page(kmapped_page);

           kunmap(kmapped_page);

           put_arg_page(kmapped_page);

           }

           return ret;

          }

          我們在前面看到,并沒有給VM映射實際的內(nèi)存,在這里COPY參數(shù)的時候,必然會引起缺頁異常,再由缺頁異常程序處理缺頁的情況.

          經(jīng)過上面的過程之后,bprm->mm表示的存儲空間如下所示:

          

          

          經(jīng)過一系統(tǒng)的初始化之后,可以尋找該文件的加載module了.這是由search_binary_handler()完成的.在深入到這段代碼之前.我們有必要討論一下linux可執(zhí)文件模塊的組織.

          

          在linux內(nèi)核,用linux_binfmt結(jié)構(gòu)來表示每一個加載模塊.它的定義如下:

          struct linux_binfmt {

           //用來構(gòu)成鏈表

           struct list_head lh;

           //所屬的module

           struct module *module;

           //加載可執(zhí)行文件

           int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);

           //加載共享庫

           int (*load_shlib)(struct file *);

           int (*core_dump)(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);

           unsigned long min_coredump; /* minimal dump size */

           int hasvdso;

          }

          結(jié)構(gòu)中的lh將之組成一個鏈表,這個鏈表的表頭是formats.

          為了說明,我們來看一下如何注冊一個可執(zhí)行文件的加載模塊.

          int register_binfmt(struct linux_binfmt * fmt)

          {

           if (!fmt)

           return -EINVAL;

           write_lock(&binfmt_lock);

           //將其添加之鏈表

           list_add(&fmt->lh, &formats);

           write_unlock(&binfmt_lock);

           return 0;

          }

          所以,在加載可執(zhí)文件的時候,只要遍歷formats這個鏈表,然后依次按module加載這個可執(zhí)行文件.這正是search_binary_handler()所做的.代碼如下:

          int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)

          {

           int try,retval;

           struct linux_binfmt *fmt;

          #ifdef __alpha__

           /* handle /sbin/loader.. */

           {

           struct exec * eh = (struct exec *) bprm->buf;

          

           if (!bprm->loader && eh->fh.f_magic == 0x183 &&

           (eh->fh.f_flags & 0x3000) == 0x3000)

           {

           struct file * file;

           unsigned long loader;

          

           allow_write_access(bprm->file);

           fput(bprm->file);

           bprm->file = NULL;

          

           loader = bprm->vma->vm_end - sizeof(void *);

          

           file = open_exec("/sbin/loader");

           retval = PTR_ERR(file);

           if (IS_ERR(file))

           return retval;

          

           /* Remember if the application is TASO. */

           bprm->sh_bang = eh->ah.entry

          

           bprm->file = file;

           bprm->loader = loader;

           retval = prepare_binprm(bprm);

           if (retval

           return retval;

           /* should call search_binary_handler recursively here,

           but it does not matter */

           }

           }

          #endif

           retval = security_bprm_check(bprm);

           if (retval)

           return retval;

          

           /* kernel module loader fixup */

           /* so we don't try to load run modprobe in kernel space. */

           set_fs(USER_DS);

          

           retval = audit_bprm(bprm);

           if (retval)

           return retval;

          

           retval = -ENOENT;

           //這里會循環(huán)兩次.待模塊加載之后再遍歷一次

           for (try=0; try

           read_lock(&binfmt_lock);

           list_for_each_entry(fmt, &formats, lh) {

           //加載函數(shù)

      &       

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多