1. 這里看到纤垂,c語(yǔ)言用函數(shù)的簡(jiǎn)單封裝逸吵,實(shí)現(xiàn)面向?qū)ο蟮闹剌d效果(這樣說(shuō)似乎也不準(zhǔn)確?)厢破,
do_execve_file(file, argv, envp)
=>__do_execve_file
do_execve(filename, argv, envp) / do_execveat(fd, filename, argv, envp, flags)
=> do_execveat_common
==>__do_execve_file
2. 可執(zhí)行文件的加載
__do_execve_file(int fd, struct filename *filename,
? ? ? ? ? ? ? ? ? ? ? ? ? ? struct user_arg_ptr argv,
? ? ? ? ? ? ? ? ? ? ? ? ? ? struct user_arg_ptr envp,
? ? ? ? ? ? ? ? ? ? ? ? ? ? int flags, struct file *file)
==? //如果文件沒打開荣瑟, 打開,這里體現(xiàn)了兼容多種場(chǎng)景
? ? ? ? if (!file)
? ? ? ? ? ? ? ? file = do_open_execat(fd, filename, flags);
==? //這里有一次做負(fù)載均衡的調(diào)用機(jī)會(huì)
? ? sched_exec();
== 這部分就是建立環(huán)境摩泪,執(zhí)行
? ? ? ? retval = bprm_mm_init(bprm);
? ? ? ? if (retval)
? ? ? ? ? ? ? ? goto out_unmark;
? ? ? ? retval = prepare_arg_pages(bprm, argv, envp);
? ? ? ? if (retval < 0)
? ? ? ? ? ? ? ? goto out;
? ? ? ? /* Set the unchanging part of bprm->cred */
? ? ? ? retval = security_bprm_creds_for_exec(bprm);
? ? ? ? if (retval)
? ? ? ? ? ? ? ? goto out;
? ? ? ? //這里的copy操作最終會(huì)memcpy()
? ? ? ? retval = copy_string_kernel(bprm->filename, bprm);
? ? ? ? if (retval < 0)
? ? ? ? ? ? ? ? goto out;
? ? ? ? bprm->exec = bprm->p;
? ? ? ? retval = copy_strings(bprm->envc, envp, bprm);
? ? ? ? if (retval < 0)
? ? ? ? ? ? ? ? goto out;
? ? ? ? //這里的copy操作最終會(huì)copy_from_user()笆焰,將數(shù)據(jù)拷貝進(jìn)內(nèi)核
? ? ? ? retval = copy_strings(bprm->argc, argv, bprm);
? ? ? ? if (retval < 0)
? ? ? ? ? ? ? ? goto out;
? ? ? ? retval = exec_binprm(bprm);
? ? ? ? === //到支持列表匹配可執(zhí)行文件格式
? ? ? ? ? search_binary_handler(struct linux_binprm *bprm)
? ? ? ? ? ===//formats是一個(gè)在內(nèi)核中已經(jīng)注冊(cè)的可支持的可執(zhí)行文件的模塊的鏈表頭, 調(diào)用__register_binfmt()會(huì)注冊(cè)新的可執(zhí)行文件
? ? ? ? ? ? ? list_for_each_entry(fmt, &formats, lh)
? ? ? ? ? ? ? fmt->load_binary(bprm)
3. 5.8內(nèi)核支持的可執(zhí)行文件
aout:
static struct linux_binfmt aout_format = {
? ? ? ? .module? ? ? ? = THIS_MODULE,
? ? ? ? .load_binary? ? = load_aout_binary,
? ? ? ? .load_shlib? ? = load_aout_library,
};
misc:
static struct linux_binfmt misc_format = {
? ? ? ? .module = THIS_MODULE,
? ? ? ? .load_binary = load_misc_binary,
};
em86:
static struct linux_binfmt em86_format = {
? ? ? ? .module? ? ? ? = THIS_MODULE,
? ? ? ? .load_binary? ? = load_em86,
};
script:
static struct linux_binfmt script_format = {
? ? ? ? .module? ? ? ? = THIS_MODULE,
? ? ? ? .load_binary? ? = load_script,
};
flat:
static struct linux_binfmt flat_format = {
? ? ? ? .module? ? ? ? = THIS_MODULE,
? ? ? ? .load_binary? ? = load_flat_binary,
? ? ? ? .core_dump? ? ? = flat_core_dump,
? ? ? ? .min_coredump? = PAGE_SIZE
};
elf_fdpic:
static struct linux_binfmt elf_fdpic_format = {
? ? ? ? .module? ? ? ? = THIS_MODULE,
? ? ? ? .load_binary? ? = load_elf_fdpic_binary,
#ifdef CONFIG_ELF_CORE
? ? ? ? .core_dump? ? ? = elf_fdpic_core_dump,
#endif
? ? ? ? .min_coredump? = ELF_EXEC_PAGESIZE,
};
elf:
static struct linux_binfmt elf_format = {
? ? ? ? .module? ? ? ? = THIS_MODULE,
? ? ? ? .load_binary? ? = load_elf_binary,
? ? ? ? .load_shlib? ? = load_elf_library,
? ? ? ? .core_dump? ? ? = elf_core_dump,
? ? ? ? .min_coredump? = ELF_EXEC_PAGESIZE,
};
4.執(zhí)行elf
load_binary()=>load_elf_binary()
==解析elf頭部
==加載文件
==價(jià)值解釋器
==執(zhí)行, start_thread(),就是把elf文件的入口地址(下一條要執(zhí)行指令的地址放入IP)见坑、堆嚷掠、棧放入寄存器
在load_elf_binary()函數(shù)中捏检,加載的可執(zhí)行文件將把當(dāng)前正在執(zhí)行的進(jìn)程的內(nèi)存空間完全覆蓋
a)如果可執(zhí)行文件是靜態(tài)鏈接的文件,進(jìn)程的IP寄存器值將被設(shè)置為main函數(shù)的入口地址不皆,從而開始新的進(jìn)程
b)如果可執(zhí)行文件是動(dòng)態(tài)鏈接的未檩,IP的值將被設(shè)置為加載器ld的入口地址,是程序的運(yùn)行由該加載器接管粟焊,ld會(huì)處理一些依賴的動(dòng)態(tài)鏈接庫(kù)相關(guān)的處理工作,使程序繼續(xù)往下執(zhí)行孙蒙,
當(dāng)前的進(jìn)程都會(huì)被新加載進(jìn)來(lái)的程序完全替換掉项棠,這也是我們最早的那個(gè)程序中第19行的信息沒有在終端上顯示的原因
總結(jié):
1)sys_execve()系統(tǒng)調(diào)用,進(jìn)入內(nèi)核空間
2) 加載新的可執(zhí)行文件挎峦,進(jìn)行可執(zhí)行性檢查
3)將新的可執(zhí)行文件映射(map)到當(dāng)前運(yùn)行進(jìn)程的進(jìn)程空間中香追,覆蓋原來(lái)的進(jìn)程數(shù)據(jù)
4)將EIP的值設(shè)置為新的可執(zhí)行程序的入口地址。
? 如果可執(zhí)行程序是靜態(tài)鏈接的程序坦胶,或不需要其他的動(dòng)態(tài)鏈接庫(kù)透典,則新的入口地址就是新的可執(zhí)行文件的main函數(shù)地址
? 如果可執(zhí)行程序還需要其他的動(dòng)態(tài)鏈接庫(kù),則入口地址是加載器ld的入口地址
5)返回用戶態(tài)顿苇,程序從新的EIP出開始繼續(xù)往下執(zhí)行峭咒。
老進(jìn)程的上下文已經(jīng)被新的進(jìn)程完全替代了,但是進(jìn)程的PID還是原來(lái)的纪岁。
從這個(gè)角度來(lái)看凑队,新的運(yùn)行進(jìn)程中已經(jīng)找不到原來(lái)的對(duì)execve調(diào)用的代碼了
所以execve函數(shù)的一個(gè)特別之處是他從來(lái)不會(huì)成功返回,而總是實(shí)現(xiàn)了一次完全的變身