a.out run!
文件分很多种,但咱可以这么分:给人看的和给机器看的。
可执行文件就是进程能认识的文件,属于后者。
机器能认识的文件也不止一种,至少内核代码里列出这么些:
[jesse@localhost linux-omapl1]$ vim ./fs/binfmt_
binfmt_aout.c binfmt_elf_fdpic.c binfmt_flat.c binfmt_script.c
binfmt_elf.c binfmt_em86.c binfmt_misc.c binfmt_som.c
elf是常用的一种,我们这里的a.out就属于此种格式。
[jesse@localhost test]$ ./a.out hello
运行a.out,而后ps -auxf
jesse 9136 0.0 0.0 7296 1996 pts/2 Ss Nov01 0:00 | | \_ bash //父进程
jesse 2320 99.3 0.0 1868 236 pts/2 R+ 16:14 0:48 | | | \_ ./a.out //子进程
有鸡才有蛋,init0之后,进程皆有老爹。显然,a.out的路径作为了一个bash的execve操作参数。
Every program is executed by means of sys_execve() system call; usually one just types program name at the shell prompt. In fact a lot of interesting things happen after you press enter. Shortly, startup process of an ELF binary can be represented with the following step-by-step figure:
Function | Kernel file | Comments |
shell | ... | on user side one types in program name and strikes enter |
execve() | ... | shell calls libc function |
sys_execve() | ... | libc calls kernel... |
sys_execve() | arch/i386/kernel/process.c | arrive to kernel side |
do_execve() | fs/exec.c | open file and do some preparation |
search_binary_handler() | fs/exec.c | find out type of executable |
load_elf_binary() | fs/binfmt_elf.c | load ELF (and needed libraries) and create user segment |
start_thread() | include/asm-i386/processor.h | and finally pass control to program code |
Figure 1. Startup process of an ELF binary.
Layout of segment created for an ELF binary shortly can be represented with Figure 2. Yellow parts represent correspondent program sections. Shared libraries are not shown here; their layout duplicates layout of program, except that they reside in earlier addresses.
0x08048000
code | .text section |
data | .data section |
bss | .bss section |
... ... ... |
free space |
stack | stack (described later) |
arguments | program arguments |
environment | program environment |
program name | filename of program (duplicated in arguments section) |
null (dword) | final dword of zero |
0xBFFFFFFF
Figure 2. Segment layout of an ELF binary.
Program takes at least two pages of memory (1 page == 4 KB), even if it consists of single sys_exit(); at least one page for ELF data (yellow color), and one for stack, arguments, and environment. Stack is growing to meet .bss; also you can use memory beyond .bss section for dynamic data allocation.
之后sys_execve的主要任务便是将可执行文件按Figure 2的方式(进程认识的格式)载入内存。
-- arch/arm/kernel/sys_arm.c --
/* sys_execve() executes a new program.
* This is called indirectly via a small wrapper
*/
asmlinkage int sys_execve( const char __user *filenamei,
const char __user *const __user *argv,
const char __user *const __user *envp,
struct pt_regs *regs)
{
int error;
char * filename;
filename = getname(filenamei); //从用户空间获得a.out的路径到内核
error = PTR_ERR(filename);
if (IS_ERR(filename))
goto out;
error = do_execve(filename, argv, envp, regs); //-->
putname(filename);
out:
return error;
}
填充struct linux_binprm :
int do_execve(const char * filename,
const char __user *const __user *argv,
const char __user *const __user *envp,
struct pt_regs * regs)
{
struct linux_binprm *bprm; //分解elf文件,保存可执行文件的一些重要参数。
struct file *file;
struct files_struct *displaced;
bool clear_in_exec;
int retval;
/* -------------------------------------------------------------------------
struct linux_binprm{
char buf[BINPRM_BUF_SIZE];
#ifdef CONFIG_MMU
struct vm_area_struct *vma; //为新进程分配的线性地址
#else
# define MAX_ARG_PAGES 32
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm; //内核的空间
unsigned long p; /* current top of mem */
unsigned int
cred_prepared:1,/* true if creds already prepared (multiple
* preps happen for interpreters) */
cap_effective:1;/* true if has elevated effective capabilities,
* false if not; except for init which inherits
* its parent's caps anyway */
#ifdef __alpha__
unsigned int taso:1;
#endif
unsigned int recursion_depth;
struct file * file;
struct cred *cred; /* new credentials */
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
unsigned int per_clear; /* bits to clear in current->personality */
int argc, envc;
char * filename; /* Name of binary as seen by procps */
char * interp; /* Name of the binary really executed. Most
of the time same as filename, but could be
different for binfmt_{misc,script} */
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
};
------------------------------------------------------------------------- */
retval = unshare_files(&displaced);
if (retval)
goto out_ret;
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); //分配结构体空间,按照惯例,之后开始依次填充
if (!bprm)
goto out_files;
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;
file = open_exec(filename); //通过名字获取struct file:do_filp_open()
retval = PTR_ERR(file);
if (IS_ERR(file))
goto out_unmark;
sched_exec();
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
retval = bprm_mm_init(bprm); //初始化bprm的mm_struct,涉及到进程的栈-->mm
if (retval)
goto out_file;
1 mm:
2
3 int bprm_mm_init(struct linux_binprm *bprm)
4 {
5 int err;
6 struct mm_struct *mm = NULL;
7
8 bprm->mm = mm = mm_alloc(); //分配mm_struct空间
9 err = -ENOMEM;
10 if (!mm)
11 goto err;
12
13 err = init_new_context(current, mm); //平台相关,初始化mm_struct,这里貌似也没做什么
14 if (err)
15 goto err;
16
17 err = __bprm_mm_init(bprm); //线性区初始化
18 if (err)
19 goto err;
20
21 return 0;
22
23 err:
24 if (mm) {
25 bprm->mm = NULL;
26 mmdrop(mm);
27 }
28
29 return err;
30 }
31
32
33
34 static int __bprm_mm_init(struct linux_binprm *bprm)
35 {
36 int err;
37 struct vm_area_struct *vma = NULL;
38 struct mm_struct *mm = bprm->mm;
39
40 bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
41 if (!vma)
42 return -ENOMEM;
43
44 down_write(&mm->mmap_sem);
45 vma->vm_mm = mm;
46
47 vma->vm_end = STACK_TOP_MAX;
48 vma->vm_start = vma->vm_end - PAGE_SIZE;
49
50 vma->vm_flags = VM_STACK_FLAGS;
51 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
52 err = insert_vm_struct(mm, vma); //将VM插入mm_struct
53 if (err)
54 goto err;
55
56 mm->stack_vm = mm->total_vm = 1;
57 up_write(&mm->mmap_sem);
58 bprm->p = vma->vm_end - sizeof(void *); //设置用户栈指针
59 return 0;
60 err:
61 up_write(&mm->mmap_sem);
62 bprm->vma = NULL;
63 kmem_cache_free(vm_area_cachep, vma);
64 return err;
65 }
/*对于用户空间参数的操作*/
bprm->argc = count(argv, MAX_ARG_STRINGS);//计算参数个数,直到为NULL
if ((retval = bprm->argc) < 0)
goto out;
bprm->envc = count(envp, MAX_ARG_STRINGS);//环境变量个数
if ((retval = bprm->envc) < 0)
goto out;
retval = prepare_binprm(bprm);//把要加载文件的前128 读入bprm->buf
if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm);//copy第一个参数filename
if (retval < 0)
goto out;
bprm->exec = bprm->p;//参数的起始地址
retval = copy_strings(bprm->envc, envp, bprm);//copy环境变量
if (retval < 0)
goto out;
retval = copy_strings(bprm->argc, argv, bprm);//copy可执行文件所带参数:argv[0]:a.out argv[1]:hello
if (retval < 0)
goto out;
至此,将用户空间参数分配到用户栈中:
做好前期准备,之后便是调用可执行文件相应的操作函数解析各自的格式。
retval = search_binary_handler(bprm,regs); //-->
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;
}
search_binary_handler()遍历format链表,找到适合的处理a.out的方式。
每一种格式对应一个结构体,结构体中包括对该格式的操作函数:
struct linux_binfmt {
struct list_head lh;
struct module *module;
int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);//加载可执行文件
int (*load_shlib)(struct file *);//加载共享库
int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */
};
内核支持许多格式,内核初始化加载可执行文件,比如ELF:
-- fs/binfmt_elf.c --
static int __init init_elf_binfmt(void)
{
return register_binfmt(&elf_format);
}
static void __exit exit_elf_binfmt(void)
{
/* Remove the COFF and ELF loaders. */
unregister_binfmt(&elf_format);
}
core_initcall(init_elf_binfmt);
module_exit(exit_elf_binfmt);
MODULE_LICENSE("GPL");
填充linux_binfmt结构体:
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,
};
-->
遍历找到适合的可执行文件处理方式:
int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
{
unsigned int depth = bprm->recursion_depth;
int try,retval;
struct linux_binfmt *fmt;
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;
for (try=0; try<2; try++) { //这里要循环两次,待模块加载之后再遍历一次
read_lock(&binfmt_lock);
list_for_each_entry(fmt, &formats, lh) { //加载适合的模块
int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
if (!fn)
continue;
if (!try_module_get(fmt->module))
continue;
read_unlock(&binfmt_lock);
retval = fn(bprm, regs); //运行加载函数,加载未成功则继续
/*
* Restore the depth counter to its starting value
* in this call, so we don't have to rely on every
* load_binary function to restore it on return.
*/
bprm->recursion_depth = depth;
if (retval >= 0) {
if (depth == 0)
tracehook_report_exec(fmt, bprm, regs);
put_binfmt(fmt);
allow_write_access(bprm->file);
if (bprm->file)
fput(bprm->file);
bprm->file = NULL;
current->did_exec = 1;
proc_exec_connector(current);
return retval;
}
read_lock(&binfmt_lock);
put_binfmt(fmt);
if (retval != -ENOEXEC || bprm->mm == NULL)
break;
if (!bprm->file) {
read_unlock(&binfmt_lock);
return retval;
}
}
read_unlock(&binfmt_lock);
if (retval != -ENOEXEC || bprm->mm == NULL) {
break;
#ifdef CONFIG_MODULES
} else {
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
if (printable(bprm->buf[0]) &&
printable(bprm->buf[1]) &&
printable(bprm->buf[2]) &&
printable(bprm->buf[3]))
break; /* -ENOEXEC */
request_module("binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
#endif
}
}
return retval;
}
具体的解析过程。
难点不在于代码,在于对elf文件格式的理解。理解之后再看代码实现,便容易许多。
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 = 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;
/*申请loc*/
struct {
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
} *loc;
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
if (!loc) {
retval = -ENOMEM;
goto out_ret;
}
#if 0
/***************************************************************************/
#define EI_NIDENT 16
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT]; //magic number
Elf32_Half e_type;
Elf32_Half e_machine; //arch:arm(40)
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;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
#define ELFMAG0 0x7f /* EI_MAG */
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EM_ARM 40
通过命令readelf -a test01显示elf头信息:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 //前八个字符明显是:7fELF
Class: ELF32
/***************************************************************************/
#endif
/* 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;
/* 读入程序段表 */
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; //bss段的起始地址
elf_brk = 0; //bss段的终止地址
start_code = ~0UL; //代码段的开始
end_code = 0; //代码段的终止
start_data = 0; //数据段的开始
end_data = 0; //数据段的终止
for (i = 0; i < loc->elf_ex.e_phnum; i++) { //扫描ELF程序段表,搜寻动态链接器定义
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;
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.
*/
if (file_permission(interpreter, MAY_READ) < 0) //动态链接程序的执行许可权
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
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 < loc->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->flags &= ~PF_FORKNOEXEC;
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);
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;
arch_pick_mmap_layout(current->mm);
/* 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;
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 mmaping the ELF image into
the correct location in memory. */
for(i = 0, elf_ppnt = elf_phdata;
i < loc->elf_ex.e_phnum; i++, elf_ppnt++) { //再次扫描程序段描述表,映射其中的程序段到进程空间
int elf_prot = 0, elf_flags;
unsigned long k, vaddr;
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_X86
load_bias = 0;
#else
load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
#endif
}
/* 将该程序段[eppnt->p_offset,eppnt->p_filesz]映射到虚存(load_bias+vaddr)开始的区域 */
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);//求出该ELF文件在用户虚存中的起始地址
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)
start_code = k; //取最小的段地址作为代码段起始
if (start_data < k)
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 < k) {
/* set_brk can never work. Avoid overflows. */
send_sig(SIGKILL, current, 0);
retval = -EINVAL;
goto out_free_dentry;
}
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段的终止
}
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;
/* 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.
*/
retval = set_brk(elf_bss, elf_brk); //建立bss的虚存映射,elf_bss是bss的开始,elf_brk是bss的结束
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 uninitialized_var(interp_map_addr);
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);
current->flags &= ~PF_FORKNOEXEC;
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);
#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. */
down_write(¤t->mm->mmap_sem);
error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
up_write(¤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 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
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;
}
Initial stack layout is very important, because it provides access to command line and environment of a program.
Here is a picture of what is on the stack when program is launched:
argc | [dword] argument counter (integer) |
argv[0] | [dword] program name (pointer) |
argv[1]
... argv[argc-1] |
[dword] program args (pointers) |
NULL | [dword] end of args (integer) |
env[0]
env[1] ... env[n] |
[dword] environment variables (pointers) |
NULL | [dword] end of environment (integer) |
Figure 3. Stack layout of an ELF binary.
参数的位置摆对,各个段映射好地址……
最后,pc寄存器便可以傻瓜式的依次读指令执行。
a.out...running...
推荐读物:
《Linker && Loader》
“ELF格式解析” 论文