? ?本文將介紹linux程序的執(zhí)行過(guò)程,并以實(shí)際問(wèn)題為切入點(diǎn)簡(jiǎn)單介紹下ELF程序的加載過(guò)程。
【正文】用后態(tài)執(zhí)行
我們知道在linux系統(tǒng)中可以通過(guò)諸如"./debug"方式執(zhí)行一個(gè)程序,那么這個(gè)程序的執(zhí)行過(guò)程中l(wèi)inux系統(tǒng)都做了什么?
本文以debug程序?yàn)槔?介紹linux內(nèi)核是如何一步步將debug進(jìn)程執(zhí)行起來(lái)的.
1 執(zhí)行過(guò)程:
以system()實(shí)現(xiàn)為例,它是一種典型的可執(zhí)行程序運(yùn)行過(guò)程:
[cpp]?view plain?copy
#include???
#include???
#include???
#include???
int?system(const?char?*?cmdstring)??
{??
pid_t?pid;??
int?status;??
if(cmdstring?==?NULL){????????
return?(1);??
}??
if((pid?=?fork())<0){??
status?=?-1;??
}??
else?if(pid?=?0){??
execl("/bin/sh",?"sh",?"-c",?cmdstring,?(char?*)0);??
-exit(127);?//子進(jìn)程正常執(zhí)行則不會(huì)執(zhí)行此語(yǔ)句??
}??
else{??
while(waitpid(pid,?&status,?0)?0){??
if(errno?!=?EINTER){??
status?=?-1;??
break;??
}??
}??
}??
return?status;??
}??
觀察上面system實(shí)現(xiàn):
1)system在當(dāng)前進(jìn)程中fork創(chuàng)建了一個(gè)子進(jìn)程,并執(zhí)行execl函數(shù)運(yùn)行可執(zhí)行文件;
2)execl/execve系列函數(shù)執(zhí)行elf文件;
實(shí)際上系統(tǒng)通過(guò)execve->do_execve_common函數(shù),將上步創(chuàng)建的子進(jìn)程,完全替換成了可執(zhí)行程序.
這個(gè)替換過(guò)程,其實(shí)也就是可執(zhí)行程序的加載過(guò)程,也是本文著重介紹的內(nèi)容.
3) execve使用實(shí)例:
#include
int execve(const char *filename, char *const argv[], char *const envp[]);?
[cpp]?view plain?copy
#include??
#include??
int?main(int?arg,?char?**args)??
{??
char?*argv[]={"ls","-al","/home/",?NULL};??
char?*envp[]={0,NULL};???
execve("/bin/ls",argv,envp);??
}??
【正文】?jī)?nèi)核態(tài)執(zhí)行
linux系統(tǒng)中,可執(zhí)行程序大多屬于ELF文件格式.
本節(jié)以實(shí)例介紹:execve("/home/debug",NULL,NULL);其中debug程序是elf格式.
當(dāng)用后執(zhí)行execve時(shí),系統(tǒng)都做了什么?下面逐層分析:
1 系統(tǒng)調(diào)用:execve->do_execve->do_execve_common
[cpp]?view plain?copy
/*?filename為可執(zhí)行文件:/home/debug;?
argv為NULL,表示可行程序不帶參數(shù);?
envp為NULL,表示沒(méi)有指定環(huán)境變量;??*/???
int?do_execve(const?char?*filename,const?char?__user?*const?__user?*__argv,??
const?char?__user?*const?__user?*__envp)??
{??
struct?user_arg_ptr?argv?=?{?.ptr.native?=?__argv?};??
struct?user_arg_ptr?envp?=?{?.ptr.native?=?__envp?};??
return?do_execve_common(filename,?argv,?envp);??
}??
2?execve->do_execve->do_execve_common()注意此時(shí)當(dāng)前進(jìn)程是上文中創(chuàng)建的子進(jìn)程。
bprm_mm_init()完成進(jìn)程地址空間vma(包括棧)的初始化.
[cpp]?view plain?copy
/*?
*?sys_execve()?executes?a?new?program.?
*/??
static?int?do_execve_common(const?char?*filename,??
struct?user_arg_ptr?argv,??
struct?user_arg_ptr?envp)??
{??
/*注意linux_binprm是核心數(shù)據(jù)結(jié)構(gòu),它保存了可執(zhí)行文件的信息;*/??
struct?linux_binprm?*bprm;??
struct?file?*file;??
struct?files_struct?*displaced;??
bool?clear_in_exec;??
int?retval;??
const?struct?cred?*cred?=?current_cred();??
/*?
*?We?move?the?actual?failure?in?case?of?RLIMIT_NPROC?excess?from?
*?set*uid()?to?execve()?because?too?many?poorly?written?programs?
*?don't?check?setuid()?return?code.??Here?we?additionally?recheck?
*?whether?NPROC?limit?is?still?exceeded.?
*/??
if?((current->flags?&?PF_NPROC_EXCEEDED)?&&??
atomic_read(&cred->user->processes)?>?rlimit(RLIMIT_NPROC))?{??
retval?=?-EAGAIN;??
goto?out_ret;??
}??
/*?We're?below?the?limit?(still?or?again),?so?we?don't?want?to?make?
*?further?execve()?calls?fail.?*/??
current->flags?&=?~PF_NPROC_EXCEEDED;??
retval?=?unshare_files(&displaced);??
if?(retval)??
goto?out_ret;??
retval?=?-ENOMEM;??
/*申請(qǐng)linux_binprm描述符,用以保存ELF可執(zhí)行文件信息*/??
bprm?=?kzalloc(sizeof(*bprm),?GFP_KERNEL);??
if?(!bprm)??
goto?out_files;??
/*生成bprm->cred即準(zhǔn)備可執(zhí)行程序運(yùn)行的用戶和組信息,主要根據(jù)當(dāng)前進(jìn)程的task->cred信息生成*/??
retval?=?prepare_bprm_creds(bprm);??
if?(retval)??
goto?out_free;??
retval?=?check_unsafe_exec(bprm);??
if?(retval?0)??
goto?out_free;??
clear_in_exec?=?retval;??
current->in_execve?=?1;??
/*?
1:打開(kāi)可執(zhí)行程序?/home/debug;?
*/??
file?=?open_exec(filename);??
retval?=?PTR_ERR(file);??
if?(IS_ERR(file))??
goto?out_unmark;??
sched_exec();??
/*bprm->file為/home/debug文件描述符*/??
bprm->file?=?file;??
/*可執(zhí)行文件名保存到bprm->filename中*/??
bprm->filename?=?filename;??
bprm->interp?=?filename;??
/*生成bprm->mm,即準(zhǔn)備可執(zhí)行程序的mm_struct信息,?
注意此時(shí)生成棧空間信息,不過(guò)后面會(huì)對(duì)??臻g再次調(diào)整?
注意此處的bprm->mm不是當(dāng)前進(jìn)程的,是bprm_mm_init申請(qǐng)的?
以后用作/home/debug進(jìn)程的mm_struct;?
*/??
retval?=?bprm_mm_init(bprm);??
if?(retval)??
goto?out_file;??
/*可執(zhí)行文件參數(shù)個(gè)數(shù),對(duì)/home/debug來(lái)說(shuō)argc=0,因?yàn)橹付▍?shù)為NULL*/??
bprm->argc?=?count(argv,?MAX_ARG_STRINGS);??
if?((retval?=?bprm->argc)?0)??
goto?out;??
/*envc=0參加bprm->argc*/??
bprm->envc?=?count(envp,?MAX_ARG_STRINGS);??
if?((retval?=?bprm->envc)?0)??
goto?out;??
/*?
elf頭保存到bprm->buf中;?
實(shí)現(xiàn)方式:?kernel_read(bprm->file,?0,?bprm->buf,?BINPRM_BUF_SIZE);//128bytes?
*/??
retval?=?prepare_binprm(bprm);??
if?(retval?0)??
goto?out;??
retval?=?copy_strings_kernel(1,?&bprm->filename,?bprm);??
if?(retval?0)??
goto?out;??
/*保存execve中指定的環(huán)境變量到linux_binprm結(jié)構(gòu)中*/??
bprm->exec?=?bprm->p;??
retval?=?copy_strings(bprm->envc,?envp,?bprm);??
if?(retval?0)??
goto?out;??
/*保存execve中指定的可執(zhí)行程序參數(shù)到linux_binprm結(jié)構(gòu)中*/??
retval?=?copy_strings(bprm->argc,?argv,?bprm);??
if?(retval?0)??
goto?out;??
/*??
該函數(shù)負(fù)責(zé)從flash上加載ELF文件:并將當(dāng)前子進(jìn)程信息替換為可執(zhí)行文件中讀取的信息.?
elf_format->load_binary=load_elf_binary->arch_setup_additional_pages?:?register_binfmt中注冊(cè)的elf_format?
->install_special_mapping->insert_vm_struct?
*/??
retval?=?search_binary_handler(bprm);??
if?(retval?0)??
goto?out;??
/*?execve?succeeded?*/??
current->fs->in_exec?=?0;??
current->in_execve?=?0;??
acct_update_integrals(current);??
free_bprm(bprm);??
if?(displaced)??
put_files_struct(displaced);??
return?retval;??
out:??
if?(bprm->mm)?{??
acct_arg_size(bprm,?0);??
mmput(bprm->mm);??
}??
out_file:??
if?(bprm->file)?{??
allow_write_access(bprm->file);??
fput(bprm->file);??
}??
out_unmark:??
if?(clear_in_exec)??
current->fs->in_exec?=?0;??
current->in_execve?=?0;??
out_free:??
free_bprm(bprm);??
out_files:??
if?(displaced)??
reset_files_struct(displaced);??
out_ret:??
return?retval;??
}??
2.1 ELF頭讀取過(guò)程:do_execve_common()->prepare_binprm()
[cpp]?view plain?copy
int?prepare_binprm(struct?linux_binprm?*bprm)??
{??
umode_t?mode;??
struct?inode?*?inode?=?file_inode(bprm->file);??
int?retval;??
mode?=?inode->i_mode;??
if?(bprm->file->f_op?==?NULL)??
return?-EACCES;??
/*?clear?any?previous?set[ug]id?data?from?a?previous?binary?*/??
bprm->cred->euid?=?current_euid();??
bprm->cred->egid?=?current_egid();??
if?(!(bprm->file->f_path.mnt->mnt_flags?&?MNT_NOSUID)?&&??
!current->no_new_privs?&&??
kuid_has_mapping(bprm->cred->user_ns,?inode->i_uid)?&&??
kgid_has_mapping(bprm->cred->user_ns,?inode->i_gid))?{??
/*?Set-uid??*/??
if?(mode?&?S_ISUID)?{??
bprm->per_clear?|=?PER_CLEAR_ON_SETID;??
bprm->cred->euid?=?inode->i_uid;??
}??
/*?Set-gid??*/??
/*?
*?If?setgid?is?set?but?no?group?execute?bit?then?this?
*?is?a?candidate?for?mandatory?locking,?not?a?setgid?
*?executable.?
*/??
if?((mode?&?(S_ISGID?|?S_IXGRP))?==?(S_ISGID?|?S_IXGRP))?{??
bprm->per_clear?|=?PER_CLEAR_ON_SETID;??
bprm->cred->egid?=?inode->i_gid;??
}??
}??
/*?fill?in?binprm?security?blob?*/??
retval?=?security_bprm_set_creds(bprm);??
if?(retval)??
return?retval;??
bprm->cred_prepared?=?1;??
memset(bprm->buf,?0,?BINPRM_BUF_SIZE);??
/*?
elf頭保存到bprm->buf中?
*/??
return?kernel_read(bprm->file,?0,?bprm->buf,?BINPRM_BUF_SIZE);??
}??
[ELF文件加載]
ELF文件格式:https://baike.baidu.com/item/ELF/7120560?fr=aladdin
3.1文件頭(Elf header)?:
Elf頭在程序的開(kāi)始部位,作為引路表描述整個(gè)ELF的文件結(jié)構(gòu),其信息大致分為四部分:一是系統(tǒng)相關(guān)信息,二是目標(biāo)文件類型,三是加載相關(guān)信息,四是鏈接相關(guān)信息。?
其中系統(tǒng)相關(guān)信息包括elf文件魔數(shù)(標(biāo)識(shí)elf文件),平臺(tái)位數(shù),數(shù)據(jù)編碼方式,elf頭部版本,硬件平臺(tái)e_machine,目標(biāo)文件版本 e_version,處理器特定標(biāo)志e_ftags:這些信息的引入極大增強(qiáng)了elf文件的可移植性,使交叉編譯成為可能。目標(biāo)文件類型用e_type的值表示,可重定位文件為1,可執(zhí)行文件為2,共享文件為3;加載相關(guān)信息有:程序進(jìn)入點(diǎn)e_entry.程序頭表偏移量e_phoff,elf頭部長(zhǎng)度 e_ehsize,程序頭表中一個(gè)條目的長(zhǎng)度e_phentsize,程序頭表?xiàng)l目數(shù)目e_phnum;鏈接相關(guān)信息有:節(jié)頭表偏移量e_shoff,節(jié)頭表中一個(gè)條目的長(zhǎng)度e_shentsize,節(jié)頭表?xiàng)l目個(gè)數(shù)e_shnum ,節(jié)頭表字符索引e shstmdx。可使用命令"readelf -h filename"來(lái)察看文件頭的內(nèi)容。?
文件頭的數(shù)據(jù)結(jié)構(gòu)如下:?
[cpp]?view plain?copy
typedef?struct?elf32_hdr{???
unsigned?char?e_ident[EI_NIDENT];???
Elf32_Half?e_type;//目標(biāo)文件類型???
Elf32_Half?e_machine;//硬件平臺(tái)???
Elf32_Word?e_version;//elf頭部版本???
Elf32_Addr?e_entry;//程序進(jìn)入點(diǎn)???
Elf32_Off?e_phoff;//程序頭表偏移量???
Elf32_Off?e_shoff;//節(jié)頭表偏移量???
Elf32_Word?e_flags;/處理器特定標(biāo)志???
Elf32_Half?e_ehsize;//elf頭部長(zhǎng)度???
Elf32_Half?e_phentsize;//程序頭表中一個(gè)條目的長(zhǎng)度???
Elf32_Half?e_phnum;//程序頭表?xiàng)l目數(shù)目???
Elf32_Half?e_shentsize;//節(jié)頭表中一個(gè)條目的長(zhǎng)度???
Elf32_Half?e_shnum;//節(jié)頭表?xiàng)l目個(gè)數(shù)???
Elf32_Half?e_shstrmdx;//節(jié)頭表字符索引???
}Elf32_Ehdr;???
程序頭表(program header table)?
程序頭表告訴系統(tǒng)如何建立一個(gè)進(jìn)程映像.它是從加載執(zhí)行的角度來(lái)看待elf文件.從它的角度看.elf文件被分成許多段,elf文件中的代碼、鏈接信息和注釋都以段的形式存放。每個(gè)段都在程序頭表中有一個(gè)表項(xiàng)描述,包含以下屬性:段的類型,段的駐留位置相對(duì)于文件開(kāi)始處的偏移,段在內(nèi)存中的首字節(jié)地址,段的物理地址,段在文件映像中的字節(jié)數(shù).段在內(nèi)存映像中的字節(jié)數(shù),段在內(nèi)存和文件中的對(duì)齊標(biāo)記。可用"readelf -l filename"察看程序頭表中的內(nèi)容。程序頭表的結(jié)構(gòu)如下:?
[cpp]?view plain?copy
typedef?struct?elf32_phdr{???
Elf32_Word?p_type;?//段的類型???
Elf32_Off?p_offset;?//段的位置相對(duì)于文件開(kāi)始處的偏移???
Elf32_Addr?p_vaddr;?//段在內(nèi)存中的首字節(jié)地址???
Elf32_Addr?p_paddr;//段的物理地址???
Elf32_Word?p_filesz;//段在文件映像中的字節(jié)數(shù)???
Elf32_Word?p_memsz;//段在內(nèi)存映像中的字節(jié)數(shù)???
Elf32_Word?p_flags;//段的標(biāo)記???
Elf32_Word?p_align;,/段在內(nèi)存中的對(duì)齊標(biāo)記???
)Elf32_Phdr;???
節(jié)頭表(section header table)?
節(jié)頭表描述程序節(jié),為編譯器和鏈接器服務(wù)。它把elf文件分成了許多節(jié).每個(gè)節(jié)保存著用于不同目的的數(shù)據(jù).這些數(shù)據(jù)可能被前面的程序頭重復(fù)使用,完成一次任務(wù)所需的信息往往被分散到不同的節(jié)里。由于節(jié)中數(shù)據(jù)的用途不同,節(jié)被分成不同的類型,每種類型的節(jié)都有自己組織數(shù)據(jù)的方式。每一個(gè)節(jié)在節(jié)頭表中都有一個(gè)表項(xiàng)描述該節(jié)的屬性,節(jié)的屬性包括小節(jié)名在字符表中的索引,類型,屬性,運(yùn)行時(shí)的虛擬地址,文件偏移,以字節(jié)為單位的大小,小節(jié)的對(duì)齊等信息,可使用"readelf -S filename"來(lái)察看節(jié)頭表的內(nèi)容。節(jié)頭表的結(jié)構(gòu)如下:?
[cpp]?view plain?copy
typedef?struct{???
Elf32_Word?sh_name;//小節(jié)名在字符表中的索引???
E1t32_Word?sh_type;//小節(jié)的類型???
Elf32_Word?sh_flags;//小節(jié)屬性???
Elf32_Addr?sh_addr;?//小節(jié)在運(yùn)行時(shí)的虛擬地址???
Elf32_Off?sh_offset;//小節(jié)的文件偏移???
Elf32_Word?sh_size;//小節(jié)的大?。宰止?jié)為單位???
Elf32_Word?sh_link;//鏈接的另外一小節(jié)的索引???
Elf32?Word?sh_info;//附加的小節(jié)信息???
Elf32?Word?sh_addralign;//小節(jié)的對(duì)齊???
Elf32?Word?sh_entsize;?//一些sections保存著一張固定大小入口的表。就像符號(hào)表???
}Elf32_Shdr;???
3.2 ELF文件加載的的實(shí)現(xiàn)代碼:
代碼流程:?do_execve_common()->search_binary_handler->load_binary=load_elf_binary()
[cpp]?view plain?copy
static?int?load_elf_binary(struct?linux_binprm?*bprm)??
{??
struct?file?*interpreter?=?NULL;?/*?to?shut?gcc?up?*/??
unsigned?long?load_addr?=?0,?load_bias?=?0;??
int?load_addr_set?=?0;??
char?*?elf_interpreter?=?NULL;??
unsigned?long?error;??
struct?elf_phdr?*elf_ppnt,?*elf_phdata;??
unsigned?long?elf_bss,?elf_brk;??
int?retval,?i;??
unsigned?int?size;??
unsigned?long?elf_entry;??
unsigned?long?interp_load_addr?=?0;??
unsigned?long?start_code,?end_code,?start_data,?end_data;??
unsigned?long?reloc_func_desc?__maybe_unused?=?0;??
int?executable_stack?=?EXSTACK_DEFAULT;??
unsigned?long?def_flags?=?0;??
struct?pt_regs?*regs?=?current_pt_regs();??
//Elf32_Ehdr??
struct?{??
struct?elfhdr?elf_ex;??
struct?elfhdr?interp_elf_ex;??
}?*loc;??
loc?=?kmalloc(sizeof(*loc),?GFP_KERNEL);??
if?(!loc)?{??
retval?=?-ENOMEM;??
goto?out_ret;??
}??
/*?
進(jìn)程的ELF頭保存在此?
*/??
/*?Get?the?exec-header?*/??
loc->elf_ex?=?*((struct?elfhdr?*)bprm->buf);??
retval?=?-ENOEXEC;??
/*?First?of?all,?some?simple?consistency?checks?*/??
if?(memcmp(loc->elf_ex.e_ident,?ELFMAG,?SELFMAG)?!=?0)??
goto?out;??
if?(loc->elf_ex.e_type?!=?ET_EXEC?&&?loc->elf_ex.e_type?!=?ET_DYN)??
goto?out;??
if?(!elf_check_arch(&loc->elf_ex))??
goto?out;??
if?(!bprm->file->f_op?||?!bprm->file->f_op->mmap)??
goto?out;??
/*?Now?read?in?all?of?the?header?information?*/??
if?(loc->elf_ex.e_phentsize?!=?sizeof(struct?elf_phdr))??
goto?out;??
if?(loc->elf_ex.e_phnum?1?||??
loc->elf_ex.e_phnum?>?65536U?/?sizeof(struct?elf_phdr))??
goto?out;??
size?=?loc->elf_ex.e_phnum?*?sizeof(struct?elf_phdr);??
retval?=?-ENOMEM;??
elf_phdata?=?kmalloc(size,?GFP_KERNEL);??
if?(!elf_phdata)??
goto?out;??
/*??
保存所有程序段到elf_phdata;注意此處elf_phdr與elfhdr的區(qū)別?
1??elf_phdr如下:程序頭?
Program?Headers:?
Type???????????Offset???VirtAddr???PhysAddr???FileSiz?MemSiz??Flg?Align?
EXIDX??????????0x000000?0x00000000?0x00000000?0x00000?0x00000?R???0x4?
PHDR???????????0x000034?0x00008034?0x00008034?0x00120?0x00120?R?E?0x4?
INTERP?????????0x000154?0x00008154?0x00008154?0x00019?0x00019?R???0x1?
[Requesting?program?interpreter:?/lib/ld-linux-armhf.so.3]?
LOAD???????????0x000000?0x00008000?0x00008000?0xb22914?0xb22914?R?E?0x8000?
LOAD???????????0xb22914?0x00b32914?0x00b32914?0x16b4a0?0x3a22d0?RW??0x8000?
DYNAMIC????????0xb25df8?0x00b35df8?0x00b35df8?0x00178?0x00178?RW??0x4?
NOTE???????????0x000170?0x00008170?0x00008170?0x00044?0x00044?R???0x4?
TLS????????????0xb22914?0x00b32914?0x00b32914?0x00000?0x00004?R???0x4?
GNU_STACK??????0x000000?0x00000000?0x00000000?0x00000?0x00000?RWE?0x4?
2??elfhder如下:elf頭?
ELF?Header:?
Magic:???7f?45?4c?46?01?01?01?00?00?00?00?00?00?00?00?00??
Class:?????????????????????????????ELF32?
Data:??????????????????????????????2's?complement,?little?endian?
Version:???????????????????????????1?(current)?
OS/ABI:????????????????????????????UNIX?-?System?V?
ABI?Version:???????????????????????0?
Type:??????????????????????????????EXEC?(Executable?file)?
Machine:???????????????????????????ARM?
Version:???????????????????????????0x1?
Entry?point?address:???????????????0x2fa31?
Start?of?program?headers:??????????52?(bytes?into?file)?
Start?of?section?headers:??????????13164260?(bytes?into?file)?
Flags:?????????????????????????????0x5000402,?has?entry?point,?Version5?EABI,??
Size?of?this?header:???????????????52?(bytes)?
Size?of?program?headers:???????????32?(bytes)?
Number?of?program?headers:?????????9?
Size?of?section?headers:???????????40?(bytes)?
Number?of?section?headers:?????????28?
Section?header?string?table?index:?27?
*/??
/*?程序段存到elf_phdata?*/??
retval?=?kernel_read(bprm->file,?loc->elf_ex.e_phoff,??
(char?*)elf_phdata,?size);??
if?(retval?!=?size)?{??
if?(retval?>=?0)??
retval?=?-EIO;??
goto?out_free_ph;??
}??
elf_ppnt?=?elf_phdata;??
elf_bss?=?0;??
elf_brk?=?0;??
start_code?=?~0UL;??
end_code?=?0;??
start_data?=?0;??
end_data?=?0;??
/*遍歷程序段,每個(gè)段32字節(jié)描述*/??
for?(i?=?0;?i?elf_ex.e_phnum;?i++)?{??
if?(elf_ppnt->p_type?==?PT_INTERP)?{??
/*?This?is?the?program?interpreter?used?for?
*?shared?libraries?-?for?now?assume?that?this?
*?is?an?a.out?format?binary?
*/??
retval?=?-ENOEXEC;??
if?(elf_ppnt->p_filesz?>?PATH_MAX?||???
elf_ppnt->p_filesz?2)??
goto?out_free_ph;??
retval?=?-ENOMEM;??
elf_interpreter?=?kmalloc(elf_ppnt->p_filesz,??
GFP_KERNEL);??
if?(!elf_interpreter)??
goto?out_free_ph;??
retval?=?kernel_read(bprm->file,?elf_ppnt->p_offset,??
elf_interpreter,??
elf_ppnt->p_filesz);??
if?(retval?!=?elf_ppnt->p_filesz)?{??
if?(retval?>=?0)??
retval?=?-EIO;??
goto?out_free_interp;??
}??
/*?make?sure?path?is?NULL?terminated?*/??
retval?=?-ENOEXEC;??
if?(elf_interpreter[elf_ppnt->p_filesz?-?1]?!=?'\0')??
goto?out_free_interp;??
/*elf_interpreter:/lib/ld-linux-armhf.so.3;bprm->filename:/bin/echo?見(jiàn)上面注釋*/??
interpreter?=?open_exec(elf_interpreter);??
retval?=?PTR_ERR(interpreter);??
if?(IS_ERR(interpreter))??
goto?out_free_interp;??
/*?
*?If?the?binary?is?not?readable?then?enforce?
*?mm->dumpable?=?0?regardless?of?the?interpreter's?
*?permissions.?
*/??
would_dump(bprm,?interpreter);??
retval?=?kernel_read(interpreter,?0,?bprm->buf,??
BINPRM_BUF_SIZE);??
if?(retval?!=?BINPRM_BUF_SIZE)?{??
if?(retval?>=?0)??
retval?=?-EIO;??
goto?out_free_dentry;??
}??
/*?Get?the?exec?headers?*/??
loc->interp_elf_ex?=?*((struct?elfhdr?*)bprm->buf);??
break;??
}??
elf_ppnt++;??
}??
elf_ppnt?=?elf_phdata;??
for?(i?=?0;?i?elf_ex.e_phnum;?i++,?elf_ppnt++)??
if?(elf_ppnt->p_type?==?PT_GNU_STACK)?{??
if?(elf_ppnt->p_flags?&?PF_X)??
executable_stack?=?EXSTACK_ENABLE_X;??
else??
executable_stack?=?EXSTACK_DISABLE_X;??
break;??
}??
/*?Some?simple?consistency?checks?for?the?interpreter?*/??
if?(elf_interpreter)?{??
retval?=?-ELIBBAD;??
/*?Not?an?ELF?interpreter?*/??
if?(memcmp(loc->interp_elf_ex.e_ident,?ELFMAG,?SELFMAG)?!=?0)??
goto?out_free_dentry;??
/*?Verify?the?interpreter?has?a?valid?arch?*/??
if?(!elf_check_arch(&loc->interp_elf_ex))??
goto?out_free_dentry;??
}??
/*?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->def_flags?=?def_flags;??
/*?Do?this?immediately,?since?STACK_TOP?as?used?in?setup_arg_pages?
may?depend?on?the?personality.??*/??
SET_PERSONALITY(loc->elf_ex);??
//executable_stack?=?EXSTACK_DISABLE_X;??
if?(elf_read_implies_exec(loc->elf_ex,?executable_stack))??
current->personality?|=?READ_IMPLIES_EXEC;??
if?(!(current->personality?&?ADDR_NO_RANDOMIZE)?&&?randomize_va_space)??
current->flags?|=?PF_RANDOMIZE;????
/*?
current切換為bprm->filename,bprm->tcomm為進(jìn)程名?
*/??
setup_new_exec(bprm);??
/*?Do?this?so?that?we?can?load?the?interpreter,?if?need?be.??We?will?
change?some?of?these?later?*/??
current->mm->free_area_cache?=?current->mm->mmap_base;??
current->mm->cached_hole_size?=?0;??
//最終指定進(jìn)程棧對(duì)應(yīng)的vma??
retval?=?setup_arg_pages(bprm,?randomize_stack_top(STACK_TOP),??
executable_stack);??
if?(retval?0)?{??
send_sig(SIGKILL,?current,?0);??
goto?out_free_dentry;??
}??
current->mm->start_stack?=?bprm->p;??
/*?Now?we?do?a?little?grungy?work?by?mmapping?the?ELF?image?into?
the?correct?location?in?memory.?*/??
for(i?=?0,?elf_ppnt?=?elf_phdata;??
i?elf_ex.e_phnum;?i++,?elf_ppnt++)?{??
int?elf_prot?=?0,?elf_flags;??
unsigned?long?k,?vaddr;??
#ifndef?gSysDebugInfoExec??
/*?
Program?Headers:?
Type???????????Offset???VirtAddr???PhysAddr???FileSiz?MemSiz??Flg?Align?
EXIDX??????????0x000000?0x00000000?0x00000000?0x00000?0x00000?R???0x4?
PHDR???????????0x000034?0x00008034?0x00008034?0x00120?0x00120?R?E?0x4?
INTERP?????????0x000154?0x00008154?0x00008154?0x00019?0x00019?R???0x1?
[Requesting?program?interpreter:?/lib/ld-linux-armhf.so.3]?
LOAD???????????0x000000?0x00008000?0x00008000?0xb22914?0xb22914?R?E?0x8000?
LOAD???????????0xb22914?0x00b32914?0x00b32914?0x16b4a0?0x3a22d0?RW??0x8000?
DYNAMIC????????0xb25df8?0x00b35df8?0x00b35df8?0x00178?0x00178?RW??0x4?
NOTE???????????0x000170?0x00008170?0x00008170?0x00044?0x00044?R???0x4?
TLS????????????0xb22914?0x00b32914?0x00b32914?0x00000?0x00004?R???0x4?
GNU_STACK??????0x000000?0x00000000?0x00000000?0x00000?0x00000?RWE?0x4?
*/??
/*?此處可以打印出/usr/bin/snmp進(jìn)程的所有程序段?
也可以通過(guò)readelf?命令讀出program?header?
*/??
#endif??
/*?
program?header中LOAD表示的就是p_type?
p_type為PT_LOAD的段需要加載進(jìn)內(nèi)存?
*/??
if?(elf_ppnt->p_type?!=?PT_LOAD)??
continue;??
if?(unlikely?(elf_brk?>?elf_bss))?{??
unsigned?long?nbyte;??
/*?There?was?a?PT_LOAD?segment?with?p_memsz?>?p_filesz?
before?this?one.?Map?anonymous?pages,?if?needed,?
and?clear?the?area.??*/??
retval?=?set_brk(elf_bss?+?load_bias,??
elf_brk?+?load_bias);??
if?(retval)?{??
send_sig(SIGKILL,?current,?0);??
goto?out_free_dentry;??
}??
nbyte?=?ELF_PAGEOFFSET(elf_bss);??
if?(nbyte)?{??
nbyte?=?ELF_MIN_ALIGN?-?nbyte;??
if?(nbyte?>?elf_brk?-?elf_bss)??
nbyte?=?elf_brk?-?elf_bss;??
if?(clear_user((void?__user?*)elf_bss?+??
load_bias,?nbyte))?{??
/*?
*?This?bss-zeroing?can?fail?if?the?ELF?
*?file?specifies?odd?protections.?So?
*?we?don't?check?the?return?value?
*/??
}??
}??
}??
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;??
vaddr?=?elf_ppnt->p_vaddr;??
if?(loc->elf_ex.e_type?==?ET_EXEC?||?load_addr_set)?{??
elf_flags?|=?MAP_FIXED;??
}?else?if?(loc->elf_ex.e_type?==?ET_DYN)?{??
/*?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.??*/??
#ifdef?CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE??
/*?Memory?randomization?might?have?been?switched?off?
*?in?runtime?via?sysctl?or?explicit?setting?of?
*?personality?flags.?
*?If?that?is?the?case,?retain?the?original?non-zero?
*?load_bias?value?in?order?to?establish?proper?
*?non-randomized?mappings.?
*/??
if?(current->flags?&?PF_RANDOMIZE)??
load_bias?=?0;??
else??
load_bias?=?ELF_PAGESTART(ELF_ET_DYN_BASE?-?vaddr);??
#else??
load_bias?=?ELF_PAGESTART(ELF_ET_DYN_BASE?-?vaddr);??
#endif??
}??
/*?
該函數(shù)增加vma;增加/proc/smaps的一個(gè)段?
*/??
error?=?elf_map(bprm->file,?load_bias?+?vaddr,?elf_ppnt,?elf_prot,?elf_flags,?0);??
if?(BAD_ADDR(error))?{??
send_sig(SIGKILL,?current,?0);??
retval?=?IS_ERR((void?*)error)????
PTR_ERR((void*)error)?:?-EINVAL;??
goto?out_free_dentry;??
}??
if?(!load_addr_set)?{??
load_addr_set?=?1;??
load_addr?=?(elf_ppnt->p_vaddr?-?elf_ppnt->p_offset);??
if?(loc->elf_ex.e_type?==?ET_DYN)?{??
load_bias?+=?error?-??
ELF_PAGESTART(load_bias?+?vaddr);??
load_addr?+=?load_bias;??
reloc_func_desc?=?load_bias;??
}??
}??
k?=?elf_ppnt->p_vaddr;??
if?(k?
start_code?=?k;??
if?(start_data?
start_data?=?k;??
/*?
*?Check?to?see?if?the?section's?size?will?overflow?the?
*?allowed?task?size.?Note?that?p_filesz?must?always?be?
*?<=?p_memsz?so?it?is?only?necessary?to?check?p_memsz.?
*/??
if?(BAD_ADDR(k)?||?elf_ppnt->p_filesz?>?elf_ppnt->p_memsz?||??
elf_ppnt->p_memsz?>?TASK_SIZE?||??
TASK_SIZE?-?elf_ppnt->p_memsz?
/*?set_brk?can?never?work.?Avoid?overflows.?*/??
send_sig(SIGKILL,?current,?0);??
retval?=?-EINVAL;??
goto?out_free_dentry;??
}??
/*?
代碼段:?
i=3:type=0x1;offset=0x0;vaddr=0x8000;paddr=0x8000;?
filesz=0xba81e0;memsz=0xba81e0;flags=0x5;align=0x8000?
數(shù)據(jù)段+程序段:?
i=4:type=0x1;offset=0xba81e0;vaddr=0xbb81e0;paddr=0xbb81e0;?
filesz=0x7799a4;memsz=0x48f922c;flags=0x6;align=0x8000?
//elf_bss=0x1331b84,elf_brk=0x54b140c?
*/??
k?=?elf_ppnt->p_vaddr?+?elf_ppnt->p_filesz;??
if?(k?>?elf_bss)??
elf_bss?=?k;??
if?((elf_ppnt->p_flags?&?PF_X)?&&?end_code?
end_code?=?k;??
if?(end_data?
end_data?=?k;??
k?=?elf_ppnt->p_vaddr?+?elf_ppnt->p_memsz;??
if?(k?>?elf_brk)??
elf_brk?=?k;??
}??
loc->elf_ex.e_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;??
#ifndef?gSysDebugInfoExec??
/*?
此時(shí)已經(jīng)有3個(gè)vma;?
elf_bss=0x105b0,elf_brk=0x105b8;bias=0x0?
start_code=0x8000,end_code=0x8490;start_data=0x10490;end_data=0x105b0?
[00008000-00009000]?,0000018f?00000875?--代碼段?
[00010000-00011000]?,0000038f?00100873?--數(shù)據(jù)段?
[7ecbb000-7ecdd000]?,0000038f?00100173?--進(jìn)程的棧?
*/??
#endif???
/*?Calling?set_brk?effectively?mmaps?the?pages?that?we?need?
*?for?the?bss?and?break?sections.??We?must?do?this?before?
*?mapping?in?the?interpreter,?to?make?sure?it?doesn't?wind?
*?up?getting?placed?where?the?bss?needs?to?go.?
*/??
/*??
1?在此為bss段申請(qǐng)?zhí)摂M地址空間,注意此處的地址空間?
為用戶態(tài)進(jìn)程的虛擬地址空間vm_brk,類似于malloc過(guò)程。?
如果申請(qǐng)的虛擬地址空間即bss段大小?大于系統(tǒng)空閑的物理內(nèi)存?
則有可能申請(qǐng)失敗,可以通過(guò)?echo?1?>?/proc/sys/vm/overcommit_memory??
去掉對(duì)內(nèi)存大小的檢測(cè)來(lái)規(guī)避失敗的風(fēng)險(xiǎn)。?
2?并未真正分配物理內(nèi)存?
3?set_brk后vma沒(méi)有變化因?yàn)閑lf_bss,elf_brk在數(shù)據(jù)段區(qū)間?
elf_bss=0x105b0,elf_brk=0x105b8;bias=0x0?
start_code=0x8000,end_code=0x8490;start_data=0x10490;end_data=0x105b0?
[00008000-00009000]?,0000018f?00000875?--代碼段?
[00010000-00011000]?,0000038f?00100873?--數(shù)據(jù)段?
[7ecbb000-7ecdd000]?,0000038f?00100173?--進(jìn)程的棧?
*/??
retval?=?set_brk(elf_bss,?elf_brk);??
if?(retval)?{??
send_sig(SIGKILL,?current,?0);??
goto?out_free_dentry;??
}??
if?(likely(elf_bss?!=?elf_brk)?&&?unlikely(padzero(elf_bss)))?{??
send_sig(SIGSEGV,?current,?0);??
retval?=?-EFAULT;?/*?Nobody?gets?to?see?this,?but..?*/??
goto?out_free_dentry;??
}??
if?(elf_interpreter)?{??
unsigned?long?interp_map_addr?=?0;??
elf_entry?=?load_elf_interp(&loc->interp_elf_ex,??
interpreter,??
&interp_map_addr,??
load_bias);??
if?(!IS_ERR((void?*)elf_entry))?{??
/*?
*?load_elf_interp()?returns?relocation?
*?adjustment?
*/??
interp_load_addr?=?elf_entry;??
elf_entry?+=?loc->interp_elf_ex.e_entry;??
}??
if?(BAD_ADDR(elf_entry))?{??
force_sig(SIGSEGV,?current);??
retval?=?IS_ERR((void?*)elf_entry)????
(int)elf_entry?:?-EINVAL;??
goto?out_free_dentry;??
}??
reloc_func_desc?=?interp_load_addr;??
allow_write_access(interpreter);??
fput(interpreter);??
kfree(elf_interpreter);??
}?else?{??
elf_entry?=?loc->elf_ex.e_entry;??
if?(BAD_ADDR(elf_entry))?{??
force_sig(SIGSEGV,?current);??
retval?=?-EINVAL;??
goto?out_free_dentry;??
}??
}??
kfree(elf_phdata);??
set_binfmt(&elf_format);??
#ifdef?ARCH_HAS_SETUP_ADDITIONAL_PAGES??
retval?=?arch_setup_additional_pages(bprm,?!!elf_interpreter);??
if?(retval?0)?{??
send_sig(SIGKILL,?current,?0);??
goto?out;??
}??
#endif?/*?ARCH_HAS_SETUP_ADDITIONAL_PAGES?*/??
install_exec_creds(bprm);??
retval?=?create_elf_tables(bprm,?&loc->elf_ex,??
load_addr,?interp_load_addr);??
if?(retval?0)?{??
send_sig(SIGKILL,?current,?0);??
goto?out;??
}??
/*?N.B.?passed_fileno?might?not?be?initialized??*/??
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;??
#ifdef?arch_randomize_brk??
if?((current->flags?&?PF_RANDOMIZE)?&&?(randomize_va_space?>?1))?{??
current->mm->brk?=?current->mm->start_brk?=??
arch_randomize_brk(current->mm);??
#ifdef?CONFIG_COMPAT_BRK??
current->brk_randomized?=?1;??
#endif??
}??
#endif??
if?(current->personality?&?MMAP_PAGE_ZERO)?{??
/*?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.?*/??
error?=?vm_mmap(NULL,?0,?PAGE_SIZE,?PROT_READ?|?PROT_EXEC,??
MAP_FIXED?|?MAP_PRIVATE,?0);??
}??
#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?DT_FINI?function,?for?
*?example.??In?addition,?it?may?also?specify?(eg,?PowerPC64?ELF)?
*?that?the?e_entry?field?is?the?address?of?the?function?descriptor?
*?for?the?startup?routine,?rather?than?the?address?of?the?startup?
*?routine?itself.??This?macro?performs?whatever?initialization?to?
*?the?regs?structure?is?required?as?well?as?any?relocations?to?the?
*?function?descriptor?entries?when?executing?dynamically?links?apps.?
*/??
ELF_PLAT_INIT(regs,?reloc_func_desc);??
#endif??
/*?可執(zhí)行程序從elf_entry開(kāi)始運(yùn)行,exec返回時(shí)pc=elf_entry出棧?*/??
start_thread(regs,?elf_entry,?bprm->p);??
retval?=?0;??
out:??
kfree(loc);??
out_ret:??
return?retval;??
/*?error?cleanup?*/??
out_free_dentry:??
allow_write_access(interpreter);??
if?(interpreter)??
fput(interpreter);??
out_free_interp:??
kfree(elf_interpreter);??
out_free_ph:??
kfree(elf_phdata);??
goto?out;??
}??
總結(jié):
此處要重點(diǎn)區(qū)分理解 ELF header和programheader的概念。
1>ELF頭描述整個(gè)程序的信息。
Praogramheader:每個(gè)程序段(比如代碼段、bss段、數(shù)據(jù)段等)都有一個(gè)這樣的頭部信息,用來(lái)描述這個(gè)程序段在文件中的大小,位置 以及放到內(nèi)存上的大小和位置信息。
程序段的頭部信息,保存在文件的e_phoff處,且程序段個(gè)數(shù)為e_phnum個(gè),如例子中為9個(gè);
2>加載可執(zhí)行的elf文件。do_execve_common->search_binary_handler
/*
load elf?load_binary=load_elf_binary->arch_setup_additional_pages
->install_special_mapping->insert_vm_struct插入虛擬內(nèi)存區(qū),即進(jìn)程地址空間.
*/
search_binary_handler(bprm)->(*fn)(struct linux_binprm *) = fmt->load_binary;
3> 加載程序program段,load_elf_binary:
1) setup_new_exec(bprm);切換當(dāng)前進(jìn)程為bprm->filename程序。
->???__set_task_comm(current,kbasename(bprm->filename), true);
設(shè)置進(jìn)程名稱、current信息,以便切換時(shí)current即為bprm->filename程序。
注意此時(shí)當(dāng)前進(jìn)程current被替換掉了。
2) elf_map函數(shù)增加vma;增加/proc/smaps的一個(gè)段
error = elf_map(bprm->file, load_bias +vaddr, elf_ppnt, elf_prot, elf_flags, 0);
3) 系統(tǒng)在load_elf_binary獲取程序段頭部信息,并進(jìn)行校驗(yàn)。
4> creds設(shè)置:
1)?prepare_exec_creds會(huì)準(zhǔn)備bprm->cred,日后install_exec_creds設(shè)置給當(dāng)前進(jìn)程。
2)setup_new_exec在install_exec_creds之前會(huì)比較bprm->cred,current->cred等
3)install_exec_creds(bprm);中安裝bprm->cred到當(dāng)前進(jìn)程的creds
之后bprm->cred = NULL;
在install_exec_creds中要比較current->cred和current->real_cred,
可以考慮cred與real_cred設(shè)置成相同。
開(kāi)放平臺(tái)的方案是在install_exec_creds->security_bprm_committing_creds(bprm);
階段將用戶id和組id改變,之前階段cred和real_cred都是0.
【實(shí)例】
舉例:一個(gè)進(jìn)程的ELF header?和program header和section header
ps:可執(zhí)行文件和動(dòng)態(tài)庫(kù)各自分別有自己的頭部信息;
#readelf –a sonia > sonia
ELF Header:
[cpp]?view plain?copy
Magic:???7f?45?4c?46?01?01?01?0000?00?00?00?00?00?00?00??
Class:????????????????????????????ELF32??
Data:?????????????????????????????2's?complement,?little?endian??
Version:??????????????????????????1?(current)??
OS/ABI:???????????????????????????UNIX?-?System?V??
ABIVersion:???????????????????????0??
Type:?????????????????????????????EXEC?(Executable?file)??
Machine:??????????????????????????ARM??
Version:??????????????????????????0x1??
Entry?point?address:??????????????0x32cfd??
Start?of?program?headers:?????????52?(bytes?into?file)??
Start?of?section?headers:?????????20061364?(bytes?into?file)??
Flags:????????????????????????????0x5000402,?has?entry?point,?Version5?EABI,???
Size?of?this?header:??????????????52?(bytes)??
Size?of?program?headers:??????????32?(bytes)??
Number?of?program?headers:????????9??
Size?of?section?headers:??????????40?(bytes)??
Number?of?section?headers:????????28??
Section?header?string?table?index:?27??
Section Headers: [25]bss段即未初始化全局變量和靜態(tài)變量保存地;查找對(duì)應(yīng)代碼段和bss段地址需要參考這個(gè)頭信息;
[cpp]?view plain?copy
[Nr]?Name??????????????Type????????????Addr?????Off???Size???ES?Flg?Lk?Inf?Al??
[0]???????????????????NULL????????????00000000?000000?000000?00??????0??0??0??
[1]?.interp???????????PROGBITS????????00008154?000154?000019?00???A?0???0??1??
[2]?.note.ABI-tag?????NOTE????????????00008170?000170?000020?00???A?0???0??4??
[3]?.note.gnu.build-i?NOTE???????????00008190?000190?000024?00???A?0???0??4??
[4]?.hash?????????????HASH????????????000081b4?0001b4?00245c?04???A?5???0??4??
[5]?.dynsym???????????DYNSYM??????????0000a610?002610?0050e0?10???A?6???1??4??
[6]?.dynstr???????????STRTAB??????????0000f6f0?0076f0?006ea4?00???A?0???0??1??
[7]?.gnu.version??????VERSYM??????????00016594?00e594?000a1c?02???A?5???0??2??
[8]?.gnu.version_r????VERNEED?????????00016fb0?00efb0?000180?00???A?6???8??4??
[9]?.rel.dyn??????????REL?????????????00017130?00f130?0001a0?08???A?5???0??4??
[10]?.rel.plt??????????REL?????????????000172d0?00f2d0?0015e0?08???A?5??12??4??
[11]?.init????????????PROGBITS????????000188b0?0108b000000c?00??AX??0??0??4??
[12]?.plt?????????????PROGBITS????????000188bc?0108bc002300?04??AX??0??0??4??
[13]?.text?????????????PROGBITS????????0001ac00?012c00?822c18?00??AX?0???0?256??
[14]?.fini????????????PROGBITS????????0083d818?835818000008?00??AX??0??0??4??
[15]?.rodata??????????PROGBITS????????0083d820?8358202b36d8?00???A??0??0??8??
[16]?.eh_frame????????PROGBITS????????00bb01dc?ba81dc?000004?00???A?0???0??4??
[17]?.tbss?????????????NOBITS??????????00bb81e0?ba81e0?000004?00?WAT??0??0??4??
[18]?.init_array??????INIT_ARRAY??????00bb81e0?ba81e0000b0c?00??WA??0??0??4??
[19]?.fini_array??????FINI_ARRAY??????00bb8cec?ba8cec000004?00??WA??0??0??4??
[20]?.jcr?????????????PROGBITS????????00bb8cf0?ba8cf0000004?00??WA??0??0??4??
[21]?.data.rel.ro?????PROGBITS????????00bb8cf8?ba8cf8002a00?00??WA??0??0??8??
[22]?.dynamic?????????DYNAMIC?????????00bbb6f8?bab6f8000178?08??WA??6??0??4??
[23]?.got?????????????PROGBITS????????00bbb870?bab8700012e0?04??WA??0??0??4??
[24]?.data????????????PROGBITS????????00bbcb50?bacb50775034?00??WA??0??0??8??
[25]?.bss??????????????NOBITS??????????01331b88?1321b84?417f884?00??WA?0???0??8??
[26]?.ARM.attributes??ARM_ATTRIBUTES??00000000?1321b84000039?00??????0???0??1??
[27]?.shstrtab?????????STRTAB??????????00000000?1321bbd?0000f5?00??????0??0??1??
ey?to?Flags:??
W(write),?A?(alloc),?X?(execute),?M?(merge),?S?(strings)??
I(info),?L?(link?order),?G?(group),?T?(TLS),?E?(exclude),?x?(unknown)??
O(extra?OS?processing?required)?o?(OS?specific),?p?(processor?specific)??
There are no section groups in this file.
Program Headers: 注意LOAD表示需要加載入內(nèi)存的程序段
Type?????????? Offset?? VirtAddr??PhysAddr?? FileSiz MemSiz? Flg Align
EXIDX????????? 0x000000 0x000000000x00000000 0x00000 0x00000 R?? 0x4
PHDR?????????? 0x000034 0x000080340x00008034 0x00120 0x00120 R E 0x4
INTERP???????? 0x000154 0x000081540x00008154 0x00019 0x00019 R?? 0x1
[Requesting program interpreter: /lib/ld-linux-armhf.so.3]
LOAD?????????? 0x000000 0x000080000x00008000 0xba81e0 0xba81e0 R E 0x8000?
->.hash + .rodata等等需要加載進(jìn)內(nèi)存的段;load_elf_binary是需要申請(qǐng)內(nèi)存,存放這些段;
動(dòng)態(tài)庫(kù)加載和可執(zhí)行文件執(zhí)行時(shí)都需要對(duì)應(yīng)的加載;
LOAD?????????? 0xba81e0 0x00bb81e0 0x00bb81e0?0x7799a4 ?0x48f922c RW?0x8000
->.data數(shù)據(jù)段+.bss段+.got段等等段的大?。?x48f922c-0x7799a4=417F888
DYNAMIC??????? 0xbab6f8 0x00bbb6f80x00bbb6f8 0x00178 0x00178 RW? 0x4
NOTE?????????? 0x000170 0x000081700x00008170 0x00044 0x00044 R?? 0x4
TLS??????????? 0xba81e0 0x00bb81e00x00bb81e0 0x00000 0x00004 R?? 0x4
GNU_STACK????? 0x000000 0x000000000x00000000 0x00000 0x00000 RWE 0x4-此段會(huì)決定棧的空間。
#/home/snmpd &
1?第一次為用戶進(jìn)程分配??臻g do_execve_common->__bprm_mm_init
并把??臻gVMA插入進(jìn)程的地址空間中。
1)?初始化過(guò)程指定程序棧的空間為vm_start:7efff000;vm_end:0x7f000000
[0x7efff000,0x7f000000],此處的棧不是當(dāng)前進(jìn)程的,是
bprm->filename=/usr/bin/snmp的。
2)?之后我們還會(huì)有對(duì)棧的空間所調(diào)整
3)?因?yàn)橛脩魬B(tài)進(jìn)程共享用戶態(tài)虛擬地址空間,所以每個(gè)進(jìn)程的棧頂?shù)刂范际沁@個(gè)0x7efff000。
~__bprm_mm_init:current:sh;filename:/home/snmpd;vm_start:7efff000;vm_end:0x7f000000
[7efff000-7f000000],0000038f 00118173
Elf頭和程序頭
type=0x2;phoff=0x34;flags=0x5000002;phnum=0x8;
type=0x200;phoff=0xfe;flags=0x0;phnum=0x1061;
lf_interpreter:/lib/ld-linux-armhf.so.3;bprm->filename:/home/snmpd
load_elf_binary-913current:sh;bprm->filename is /home/snmpd bprm->tcomm=snmpd
current->top_stack=0x7effff91
進(jìn)程地址空間的棧區(qū)間調(diào)整之前
[7effe000-7f000000],0000038f 00118173
2?調(diào)整??臻g的大小setup_arg_pages,調(diào)整后為[7ea7a000-7ea9c000]
do_execve_common->search_binary_handler-> load_elf_binary->setup_arg_pages
stack_top=0x7ea9c000;mmap_min_addr=4096
6i=0:6type=0x70000001;offset=0x484;vaddr=0x8484;paddr=0x8484;filesz=0x8;memsz=0x8;flags=0x4;align=0x4
6i=1:6type=0x6;offset=0x34;vaddr=0x8034;paddr=0x8034;filesz=0x100;memsz=0x100;flags=0x5;align=0x4
6i=2:6type=0x3;offset=0x134;vaddr=0x8134;paddr=0x8134;filesz=0x19;memsz=0x19;flags=0x4;align=0x1
6i=3:6type=0x1;offset=0x0;vaddr=0x8000;paddr=0x8000;filesz=0x490;memsz=0x490;flags=0x5;align=0x8000
6load_elf_binary-1039i=3 current:snmpd;bprm->filename is /home/snmpd,e_type:2,p_type:1
進(jìn)程地址空間增加代碼段之前,??臻g調(diào)整之后的棧為如下區(qū)間:
[7ea7a000-7ea9c000] ,0000038f 00100173 –調(diào)整后的棧
3?進(jìn)程地址空間中增加代碼段elf_map,調(diào)整后為[00008000-00009000]
do_execve_common->search_binary_handler-> load_elf_binary->?elf_map
elf_map-342
current:snmpd;addr:0x8000;size:0x1000;p_filesz:0x490;p_offset:0x0;p_vaddr:0x8000
load_elf_binary-1058current:snmpd;bprm->filename is /home/snmpd
[00008000-00009000],0000018f 00000875—代碼段
[7ea7a000-7ea9c000],0000038f 00100173 –棧
6i=4:6type=0x1;offset=0x490;vaddr=0x10490;paddr=0x10490;filesz=0x120;memsz=0x128;flags=0x6;align=0x8000
進(jìn)程地址空間增加數(shù)據(jù)段之前
6load_elf_binary-1039i=4 current:snmpd;bprm->filename is /home/snmpd,e_type:2,p_type:1
[00008000-00009000],0000018f 00000875 – 代碼段
[7ea7a000-7ea9c000],0000038f 00100173 –棧
4?進(jìn)程地址空間中增加程序段elf_map,調(diào)整后為[00010000-00011000]
do_execve_common->search_binary_handler-> load_elf_binary->?elf_map
elf_map-342current:snmpd;addr:0x10000;size:0x1000;p_filesz:0x120;p_offset:0x490;p_vaddr:0x10490
6load_elf_binary-1058current:snmpd;bprm->filename is /home/snmpd
[00008000-00009000],0000018f 00000875 –代碼段
[00010000-00011000],0000038f 00100873 –數(shù)據(jù)段
[7ea7a000-7ea9c000],0000038f 00100173—棧
6i=5:6type=0x2;offset=0x49c;vaddr=0x1049c;paddr=0x1049c;filesz=0xe8;memsz=0xe8;flags=0x6;align=0x4
6i=6:6type=0x4;offset=0x150;vaddr=0x8150;paddr=0x8150;filesz=0x44;memsz=0x44;flags=0x4;align=0x4
6i=7:6type=0x6474e551;offset=0x0;vaddr=0x0;paddr=0x0;filesz=0x0;memsz=0x0;flags=0x6;align=0x4
進(jìn)程地址空間中增加bss段之前
load_elf_binary-1136current:snmpd;bprm->filename is/home/snmpd,elf_bss=0x105b0,elf_brk=0x105b8;bias=0x0
6start_code=0x8000,end_code=0x8490;start_data=0x10490;end_data=0x105b0
[00008000-00009000],0000018f 00000875
[00010000-00011000],0000038f 00100873
[7ea7a000-7ea9c000],0000038f 00100173
5?進(jìn)程地址空間中增加bss段set_brk,調(diào)整后為[00010000-00011000]
do_execve_common->search_binary_handler-> load_elf_binary->?set_brk
注意bss段增加之后,程序段并沒(méi)有變化,因?yàn)閎ss段在data段的區(qū)間內(nèi)
load_elf_binary-1168current:snmpd;bprm->filename is /home/snmpd
[00008000-00009000],0000018f 00000875
[00010000-00011000],0000038f 00100873
[7ea7a000-7ea9c000],0000038f 00100173
6?進(jìn)程地址空間中增加其余段load_elf_interp,調(diào)整后為:
do_execve_common->search_binary_handler-> load_elf_binary->?load_elf_interp
6elf_map-342current:snmpd;addr:0x0 ;size:0x1a000;p_filesz:0x19308;p_offset:0x0;p_vaddr:0x0
6elf_map-342current:snmpd;addr:0x76f24000;size:0x2000;p_filesz:0xb50;p_offset:0x19d38;p_vaddr:0x21d38
分析對(duì)比smaps:
6load_elf_binary-1226current:snmpd;bprm->filename is /home/snmpd
[00008000-00009000],0000018f 00000875 –代碼段
[00010000-00011000],0000038f 00100873 –數(shù)據(jù)段/bss段
[76f03000-76f1d000],0000018f 00000875
[76f23000-76f24000],0000018f 00040075
[76f24000-76f26000],0000038f 00100873
[7ea7a000-7ea9c000],0000038f 00100173 –棧
[00008000-00009000],0000018f 00000875
[00010000-00011000],0000038f 00100873
[76f03000-76f1d000],0000018f 00000875
[76f23000-76f24000],0000018f 00040075
[76f24000-76f26000],0000038f 00100873
[7ea7a000-7ea9c000],0000038f 00100173
buf=0x103d008
以上分析對(duì)比smaps:
#cat /proc/239/smaps
[cpp]?view plain?copy
00008000-00009000r-xp?00000000?00:0e?23330821??/home/snmpd?-代碼段??
Size:??????????????????4?kB??
Rss:???????????????????4?kB??
Pss:???????????????????4?kB??
Shared_Clean:??????????0?kB??
Shared_Dirty:??????????0?kB??
Private_Clean:?????????4?kB??
Private_Dirty:?????????0?kB??
Referenced:????????????4?kB??
Anonymous:?????????????0?kB??
AnonHugePages:?????????0?kB??
Swap:??????????????????0?kB??
KernelPageSize:????????4?kB??
MMUPageSize:???????????4?kB??
Locked:????????????????0?kB??
VmFlags:rd?ex?mr?mw?me?dw??
00010000-00011000rw-p?00000000?00:0e?23330821??/home/snmpd–數(shù)據(jù)段/bss段??
Size:??????????????????4?kB??
Rss:???????????????????4?kB??
Pss:???????????????????4?kB??
Shared_Clean:??????????0?kB??
Shared_Dirty:??????????0?kB??
Private_Clean:?????????0?kB??
Private_Dirty:?????????4?kB??
Referenced:????????????4?kB??
Anonymous:?????????????4?kB??
AnonHugePages:?????????0?kB??
Swap:??????????????????0?kB??
KernelPageSize:????????4?kB??
MMUPageSize:???????????4?kB??
Locked:????????????????0?kB??
VmFlags:rd?wr?mr?mw?me?dw?ac??
0103d000-01060000rw-p?00000000?00:00?0??????????[heap]??
Size:????????????????140?kB??
Rss:???????????????????8?kB??
Pss:???????????????????8?kB??
Shared_Clean:??????????0?kB??
Shared_Dirty:??????????0?kB??
Private_Clean:?????????0?kB??
Private_Dirty:?????????8?kB??
Referenced:????????????8?kB??
Anonymous:?????????????8?kB??
AnonHugePages:?????????0?kB??
Swap:??????????????????0?kB??
KernelPageSize:????????4?kB??
MMUPageSize:???????????4?kB??
Locked:????????????????0?kB??
VmFlags:rd?wr?mr?mw?me?ac??
76dff000-76ef6000r-xp?00000000?1f:05?154???????/lib/libc-2.19-2014.06.so??
Size:????????????????988?kB??
Rss:?????????????????236?kB??
Pss:??????????????????34?kB??
Shared_Clean:????????236?kB??
Shared_Dirty:??????????0?kB??
Private_Clean:?????????0?kB??
Private_Dirty:?????????0?kB??
Referenced:??????????236?kB??
Anonymous:?????????????0?kB??
AnonHugePages:?????????0?kB??
Swap:??????????????????0?kB??
KernelPageSize:????????4?kB??
MMUPageSize:???????????4?kB??
Locked:????????????????0?kB??
VmFlags:rd?ex?mr?mw?me??
【動(dòng)態(tài)庫(kù)加載】
對(duì)于同一個(gè)動(dòng)態(tài)庫(kù)來(lái)說(shuō),不同進(jìn)程中的vma區(qū)間不同,但對(duì)應(yīng)相同的文件頁(yè),所以一旦動(dòng)態(tài)庫(kù)被一個(gè)進(jìn)程加載到了內(nèi)存中,其他進(jìn)程不用再次從flash上加載,也就是說(shuō)這個(gè)動(dòng)態(tài)庫(kù)是共享的.
不同進(jìn)程中同一動(dòng)態(tài)庫(kù)對(duì)應(yīng)的虛擬地址雖然不同,但動(dòng)態(tài)庫(kù)加載進(jìn)內(nèi)存的物理偏移地址是相同的.當(dāng)進(jìn)程訪問(wèn)動(dòng)態(tài)庫(kù)中的一個(gè)函數(shù)時(shí),這個(gè)函數(shù)的地址如果未經(jīng)過(guò)分頁(yè)映射,即有可能初次訪問(wèn),則觸發(fā)缺頁(yè)異常,文件頁(yè)的缺頁(yè)異常處理流程會(huì)根據(jù)這個(gè)函數(shù)在動(dòng)態(tài)庫(kù)中的偏移地址(即時(shí)不同進(jìn)程,這個(gè)偏移地址也是相同的)找到對(duì)應(yīng)文件頁(yè),并判斷是否需要從flash中讀數(shù)據(jù)到該文件頁(yè).
1 加載動(dòng)態(tài)庫(kù)的系統(tǒng)調(diào)用:sys_userlib->load_shlib=load_elf_library
2 查找動(dòng)態(tài)庫(kù)的bss段信息:binfmt_elf.c:
/*file表示動(dòng)態(tài)庫(kù)文件;shdata輸出變量,區(qū)section頭部信息*/
[cpp]?view plain?copy
int?load_elf_library_section(struct?file?*file,struct?elf_shdr?*shdata)??
{??
struct?elf_shdr?*elf_shdata;??
struct?elf_shdr?*eppnt;??
unsigned?long?elf_bss,bss,len;??
int?retval,error,i,j;??
struct?elfhdr?elf_ex;??
/*讀取elf頭*/??
retval?=?kernel_read(file,0,(char?*)&elf_ex,sizeof(elf_ex));??
/*所有section頭的總大小*/??
j?=sizeof(struct?elf_shdr)*elf_ex.e_shnum;??
elf_shdata?=?kmalloc(j,GFP_KERNEL);??
eppnt?=?elf_shdata;??
/*讀取section?header*/??
retval?=?kernel_read(file,elf_ex.e_shoff,(char?*)eppnt,j);??
for(j=0,i=0;i
{??
if(SH_NOBITS==eppnt->sh_type)??
{??
printk("bss?section:\n");??
memcpy(shdata,eppnt,sizeof(struct?elf_shdr));??
}??
dump_elf_shdr(eppnt);//打印section頭部信息??
eppnt++;??
}??
kfree(elf_shdata);??
return?error;??
}??
filemap_fault()文件頁(yè)缺頁(yè)異常處理中可以根據(jù)動(dòng)態(tài)庫(kù)的名稱,導(dǎo)出動(dòng)態(tài)庫(kù)的指定段信息(如bss、data段等) .
3 動(dòng)態(tài)庫(kù)加載過(guò)程:
1>可以通過(guò)命令:
#strace ls ?--查看動(dòng)態(tài)庫(kù)加載過(guò)程,一般流程open(".so")->mmap();
2>文件頁(yè)缺頁(yè)異常中真正從flash上獲取動(dòng)態(tài)庫(kù)內(nèi)容:filemap_fault;
3>具體過(guò)程可以參考如下博文:
linux文件讀取過(guò)程:http://blog.csdn.net/eleven_xiy/article/details/73609237
linux內(nèi)存回收機(jī)制:http://blog.csdn.net/eleven_xiy/article/details/75195490;
4?動(dòng)態(tài)庫(kù)鏈接過(guò)程舉例:
4.1 基本信息
程序名:debug;連接動(dòng)態(tài)庫(kù):libdebug.so;
窗口終端環(huán)境變量:
#export
export HOME='/'
export PATH='/sbin:/usr/sbin:/bin:/usr/bin'
export PWD='/'
執(zhí)行過(guò)程:./debug sh進(jìn)程中執(zhí)行execve(debug);
execve返回時(shí)debug進(jìn)程開(kāi)始執(zhí)行;
debug進(jìn)程首先鏈接動(dòng)態(tài)庫(kù),默認(rèn)嘗試路徑open(/lib/libdebug.so);open(/usr/lib/libdebug.so);
如果配置export LD_LIBRARY_PATH='/mnt/mtd'?
則嘗試open(/mnt/mtd/libdebug.so);open(/lib/libdebug.so);open(/usr/lib/libdebug.so);
debug進(jìn)程執(zhí)行main入口函數(shù);
telnet終端環(huán)境變量,遵循/etc/profile中配置:
export HOME='/'
export LD_LIBRARY_PATH='/usr/local/lib:/usr/lib:/mnt/mtd/'
export PATH='/sbin:/usr/sbin:/bin:/usr/bin'
export PWD='/'
可執(zhí)行程序鏈接動(dòng)態(tài)庫(kù)是在elf程序exec執(zhí)行之后,main入口函數(shù)執(zhí)行之前;
編譯過(guò)程指定鏈接路徑為:/lib;/usr/lib + LD_LIBRARY_PATH;
可執(zhí)行文件中定位動(dòng)態(tài)庫(kù)中符號(hào):?GOT和PLT原理簡(jiǎn)析
【總結(jié)】
本文介紹了可執(zhí)行文件和動(dòng)態(tài)庫(kù)加載過(guò)程,舉例說(shuō)明了ELF 文件的elf頭,程序頭(program header)和section header. 值得注意的是 :
1 程序頭中的LOAD(PT_LOAD)段,是需要加載進(jìn)內(nèi)存的,且load_elf_binary/load_elf_library過(guò)程都需要加載,這個(gè)段中不只包含data和bss段,
可以通過(guò)readelf -a查看Section to Segment mapping中表明了LOAD包含的段;
2 section header中真正指明了程序的數(shù)據(jù)段、bss段(SH_NOBITS).
3 ELF程序執(zhí)行過(guò)程中,讀取ELF各頭部信息,并逐步替換掉當(dāng)前進(jìn)程(包括進(jìn)程名,進(jìn)程地址空間等),最后切換到ELF程序執(zhí)行.
當(dāng)前進(jìn)程不需要主動(dòng)退出,就切換到ELF程序中,因?yàn)楫?dāng)前進(jìn)程所有信息都被ELF替換掉了.
?
評(píng)論
查看更多