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 = 0;
int executable_stack = EXSTACK_DEFAULT;
unsigned long def_flags = 0;
struct {
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
} *loc;
定义了loc结构体,其中elfhdr就是 elf32_hdr
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
if (!loc) {
retval = -ENOMEM; 错误代码:ENOMEM:核心内存不足
goto out_ret;
}
void *kmalloc(size_t size, int flags);
size要分配内存的大小. 以字节为单位.
flags要分配内存的类型。
kmalloc函数返回的是虚拟地址(线性地址). kmalloc特殊之处在于它分配的内存是物理上连续的
GFP_KERNEL是内核内存分配时最常用的,无内存可用时可引起休眠。
看上去就是给loc分配了一个虚拟地址
/* 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;
e_type = ET_EXEC为2(可执行文件 ) = ET_DYN为3(动态链接库文件)
EF_CRIS_VARIANT_ANY_V0_V10 为 0x00000000 判断3,2,1是否为0
EF_CRIS_VARIANT_COMMON_V10_V32 0x00000004 0100 判断2位是否为1,第3,1位是否为0
也就是第3位第1位是否为0
/* Now read in all of the header information */
if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr)) 确认e_phentsize 大小
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;
}
为什么找不到kernel_read的资料。。。
这个 struct elf_phdr 和之前的 elf32_hdr 不是同一个,差点弄错了 ,这样就可以理解了。
elf_bss = 0;
elf_brk = 0;
start_code = ~0UL;
end_code = 0;
start_data = 0;
end_data = 0;
变量的初始化 0UL是无符号长整型 0, ~ 表示按位取反,即:0xffff
原来这是一个0啊,我以为是O
for (i = 0; i < loc->elf_ex.e_phnum; i++) { 按程序个数一个一个找吗
if (elf_ppnt->p_type == PT_INTERP) { 之前elf_ppnt = elf_phdata; -->elf_phdata=kmalloc(size, GFP_KERNEL); 这个elf_ppnt就是另一份映射吧
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/PT_INTERP是3,于是判断是否为动态链接库
retval = -ENOEXEC;
if (elf_ppnt->p_filesz > PATH_MAX ||
elf_ppnt->p_filesz < 2) PATH_MAX 4096
goto out_free_ph;
retval = -ENOMEM;
elf_interpreter = kmalloc(elf_ppnt->p_filesz,
GFP_KERNEL); 之前elf_phdata = kmalloc(size, GFP_KERNEL); 这里分配内存大小应该也一样吧
if (!elf_interpreter)
goto out_free_ph;
retval = kernel_read(bprm->file, elf_ppnt->p_offset,
elf_interpreter,
elf_ppnt->p_filesz); 这里又在读取了,比较一下之前的①都是bprm->file,②elf_ppnt->p_offset和loc->elf_ex.e_phoff
elf_ppnt= elf_phdata;elf_phdata又是loc->elf_ex分配的这两个也是一样的吧?③elf_interpreter存放地址④elf_ppnt->p_filesz和size也一样吧。
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;
又来了 open_exec
* 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;
}
file_permis sion - -> inode_permission(file->f_path.dentry->d_inode, mask);
通过kernel_read()读入其开头128个字节,这就是映像的头部。
/* Get the exec headers */
loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
break;
}
elf_ppnt++;
}
又获取了头部,这么翻来覆去的搞,指是把东西读出来么,循环到这里结束了。
读出 来判断是否有动态 链 接库?
elf_ppnt = elf_phdata; 为什么又赋一遍,上面的循环elf_ppnt已经到底了,又让它回来了
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;
}
又检查是否是可执 行文件, elf_interpreter怎么是动态链接器 呢
/* Flush all traces of the currently running executable */
retval = flush_old_exec(bprm);
if (retval)
goto out_free_dentry;
flush_old_exec()把当前进程用户空间的页面都释放了
/* 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;
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;
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 < 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; mmap的flag?
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) { 如果为共享库,要加偏移量,e_type 为3就是共享库
/* 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
}
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不是本来就是0吗
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)
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; 段虚拟地址加上段大小就到了段尾
if (k > elf_bss)
elf_bss = k; 后面接elf_bss?
if ((elf_ppnt->p_flags & PF_X) && end_code < k) ELF 段标志可执行 并且。。。
end_code = k;
if (end_data < k)
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_bi
阅读(444) | 评论(0) | 转发(2) |
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
{
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 = 0;
int executable_stack = EXSTACK_DEFAULT;
unsigned long def_flags = 0;
struct {
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
} *loc;
定义了loc结构体,其中elfhdr就是 elf32_hdr
typedef struct elf32_hdr{unsigned char e_ident[EI_NIDENT];Elf32_Half e_type; ELF文件类型,1表示此文件是重定位文件,2表示可执行文件,3表示动态连接库Elf32_Half e_machine; CPU类型,它指出了此文件使用何种指令集。如果是Intel 0x386 CPU此值为3Elf32_Word e_version; ELF文件版本,为1。Elf32_Addr e_entry; /* Entry point */映像的程序入口Elf32_Off e_phoff; 这个是Program Header offset 程序头位移量Elf32_Off e_shoff; 这个是Section Header offset 节头偏移量Elf32_Word e_flags; 处理器特定标志Elf32_Half e_ehsize; ELF头部长度Elf32_Half e_phentsize; 数组元素(表项)的大小Elf32_Half e_phnum; Program Header numberElf32_Half e_shentsize; 数组元素(表项)的大小Elf32_Half e_shnum; Section Header numberElf32_Half e_shstrndx; 节头部字符表索引} Elf32_Ehdr; //此结构体一共52个字节
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
if (!loc) {
retval = -ENOMEM; 错误代码:ENOMEM:核心内存不足
goto out_ret;
}
void *kmalloc(size_t size, int flags);
size要分配内存的大小. 以字节为单位.
flags要分配内存的类型。
kmalloc函数返回的是虚拟地址(线性地址). kmalloc特殊之处在于它分配的内存是物理上连续的
GFP_KERNEL是内核内存分配时最常用的,无内存可用时可引起休眠。
看上去就是给loc分配了一个虚拟地址
/* Get the exec-header */
loc->elf_ex = *((struct elfhdr *)bprm->buf);
retval = -ENOEXEC; 不是出错才赋值吗
/*
* This structure is used to hold the arguments that are used when loading binaries.
*/这种结构是用来装是用来装载二进制时的参数。
该函数用到了一个类型为linux_binprm的结构体来保存要要执行的文件相关的信息
struct linux_binprm{
char buf[BINPRM_BUF_SIZE]; 保存可执行文件的头128字节
#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,
cap_effective:1;
#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; 要执行的文件的真实名称,通常和filename相同
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
};
保存可执行文件的头128字节到
loc->elf_ex
/* First of all, some simple consistency checks */
if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out;
int memcmp(const void *buf1, const void *buf2, unsigned int count);
loc->elf_ex.e_ident 确认是否为"\177ELF"的ELF格式文件 SELFMAG是4
这里\177为8进制,十六进制为0x7f,后面的为'E','L','F'
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;
e_type = ET_EXEC为2(可执行文件 ) = ET_DYN为3(动态链接库文件)
#define elf_check_arch(x) \EF_CRIS_VARIANT_MASK 为0x0000000e 1110
((x)->e_machine == EM_CRIS \ 确认处理器
&& ((((x)->e_flags & EF_CRIS_VARIANT_MASK) == EF_CRIS_VARIANT_ANY_V0_V10 \
|| (((x)->e_flags & EF_CRIS_VARIANT_MASK) == EF_CRIS_VARIANT_COMMON_V10_V32))))
EF_CRIS_VARIANT_ANY_V0_V10 为 0x00000000 判断3,2,1是否为0
EF_CRIS_VARIANT_COMMON_V10_V32 0x00000004 0100 判断2位是否为1,第3,1位是否为0
也就是第3位第1位是否为0
/* Now read in all of the header information */
if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr)) 确认e_phentsize 大小
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;
}
为什么找不到kernel_read的资料。。。
int kernel_read(struct file *file, loff_t offset,
char *addr, unsigned long count)
{
mm_segment_t old_fs;
loff_t pos = offset;
int result;
old_fs = get_fs();
set_fs(get_ds());
/* The cast to a user pointer is valid due to the set_fs() */
result = vfs_read(file, (void __user *)addr, count, &pos);
set_fs(old_fs);
return result;
}
kernel_read就是把那个loc映像文件的程序表头读入了。
然后就是get_fs了
#define get_fs() (current_thread_info()->addr_limit) 看名字就是获取当前的地址访问限制值。
然后是set_fs
set_fs(get_ds()); #define get_ds() (KERNEL_DS) #define KERNEL_DS ((mm_segment_t){0})看不懂
后面vfs_read() 应该是内核读取到用户空间吧,放到addr的字符串指针? 返回的读取长度?
这个 struct elf_phdr 和之前的 elf32_hdr 不是同一个,差点弄错了 ,这样就可以理解了。
typedef struct elf32_phdr {elf_ppnt = elf_phdata;
Elf32_Word p_type; 部的类型
Elf32_Off p_offset; 该段在文件中的偏移。这个偏移是相对于整个文件的。
Elf32_Addr p_vaddr; 该段加载后在进程空间中占用的内存起始地址。
Elf32_Addr p_paddr; 该段的物理地地址。这个字段被忽略,因为在多数现代操作系统下物理地址是进程无法触及的。
Elf32_Word p_filesz; 该段在文件中占用的字节大小,有些段可能在文件中不存在但却占内存空间,此时这个字段为0。
Elf32_Word p_memsz;该段在内存中占用的字节大小,有些段可能仅存在于文件中而不被加载到内存,此时这个字段为0。
Elf32_Word p_flags;段的属性。它用每一个二进制位表示一种属,相应位为1表示含有相应的属性,为0表示不含那种属性。其中最低位是可执行位,次低位是可写位,第三低位是可读位。
Elf32_Word p_align; 对齐。
} Elf32_Phdr;
elf_bss = 0;
elf_brk = 0;
start_code = ~0UL;
end_code = 0;
start_data = 0;
end_data = 0;
变量的初始化 0UL是无符号长整型 0, ~ 表示按位取反,即:0xffff
原来这是一个0啊,我以为是O
for (i = 0; i < loc->elf_ex.e_phnum; i++) { 按程序个数一个一个找吗
if (elf_ppnt->p_type == PT_INTERP) { 之前elf_ppnt = elf_phdata; -->elf_phdata=kmalloc(size, GFP_KERNEL); 这个elf_ppnt就是另一份映射吧
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/PT_INTERP是3,于是判断是否为动态链接库
retval = -ENOEXEC;
if (elf_ppnt->p_filesz > PATH_MAX ||
elf_ppnt->p_filesz < 2) PATH_MAX 4096
goto out_free_ph;
retval = -ENOMEM;
elf_interpreter = kmalloc(elf_ppnt->p_filesz,
GFP_KERNEL); 之前elf_phdata = kmalloc(size, GFP_KERNEL); 这里分配内存大小应该也一样吧
if (!elf_interpreter)
goto out_free_ph;
retval = kernel_read(bprm->file, elf_ppnt->p_offset,
elf_interpreter,
elf_ppnt->p_filesz); 这里又在读取了,比较一下之前的①都是bprm->file,②elf_ppnt->p_offset和loc->elf_ex.e_phoff
elf_ppnt= elf_phdata;elf_phdata又是loc->elf_ex分配的这两个也是一样的吧?③elf_interpreter存放地址④elf_ppnt->p_filesz和size也一样吧。
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;
又来了 open_exec
struct file *open_exec(const char *name)
{
struct file *file;
int err;
这个就是最主要的函数吧
file = do_filp_open(AT_FDCWD, name, AT_FDCWD -100 当前路径吧?相对路径名
O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0, O_LARGEFILE 大文件?O_RDONLY 可读写FMODE_EXEC文件被打开
MAY_EXEC | MAY_OPEN); MAY_EXEC允许本地用户绕过限制执行文件 MAY_OPEN.......
if (IS_ERR(file))
goto out;
err = -EACCES; #define EACCES 13 Permission denied!!!
if (!S_ISREG(file->f_path.dentry->d_inode->i_mode))
goto exit;
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)
goto exit;
fsnotify_open(file->f_path.dentry);
err = deny_write_access(file);
if (err)
goto exit;
out:
return file;
exit:
fput(file);
return ERR_PTR(err);
}
struct file *do_filp_open(int dfd, const char *pathname,
int open_flag, int mode, int acc_mode)
这个函数再说吧http://blog.csdn.net/f413933206/article/details/5701913
do_filp_open先是把函数中的open_flag确认各种flag保证文件的权限,防止被破坏。然后link_path_walk()将用户传进来的字符串表示的文件路径转换成一个dentry结构,并建立好相应的inode和file结构。path_init为查找作准备工作,path_walk真正上路查找,这两个函数联合起来根据一段路径名找到对应的dentry
好像不搞懂dentry不行啊
struct dentry {
atomic_t d_count; 目录项对象使用计数器
unsigned int d_flags; /* protected by d_lock */ 目录项标志
spinlock_t d_lock; /* per dentry lock */
int d_mounted;
struct inode *d_inode; /* Where the name belongs to - NULL is 与文件名关联的索引节点
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */ 散列表表项的指针
struct dentry *d_parent; /* parent directory */ 父目录的目录项对象
struct qstr d_name;
struct list_head d_lru; /* LRU list */ 未使用链表的指针
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */ 父目录中目录项对象的链表的指针
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */ 对目录而言,表示子目录目录项对象的链表
struct list_head d_alias; /* inode alias list */ 相关索引节点(别名)的链表
unsigned long d_time; /* used by d_revalidate */
const struct dentry_operations *d_op; 目录项方法
struct super_block *d_sb; /* The root of the dentry tree */ 文件的超级块对象
void *d_fsdata; /* fs-specific data */ 与文件系统相关的数据
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ 存放短文件名
};
d_count有三种状态,回收内存的时候会考虑它
/*
struct inode {
struct hlist_node i_hash; 哈希表
struct list_head i_list; /* backing dev IO list */ 索引节点链表
struct list_head i_sb_list;
struct list_head i_dentry;,怎么两边都有目录项链表
unsigned long i_ino; 节点号
atomic_t i_count; 引用记数
unsigned int i_nlink;
uid_t i_uid; 使用者id
gid_t i_gid; 使用者id组
dev_t i_rdev; 实设备标识符
unsigned int i_blkbits; 以位为单位的块大小
u64 i_version; 版本号
loff_t i_size; 以字节为单位的文件大小
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
struct timespec i_atime; 最后访问时间
struct timespec i_mtime; 最后修改(modify)时间
struct timespec i_ctime; 最后改变(change)时间
blkcnt_t i_blocks; 文件的块数
unsigned short i_bytes; 使用的字节数
umode_t i_mode;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ 自旋锁
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem; 索引节点信号量
const struct inode_operations *i_op; 索引节点操作表
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ 默认的索引节点操作
struct super_block *i_sb; 相关的超级块
struct file_lock *i_flock; 文件锁链表
struct address_space *i_mapping; 相关的地址映射
struct address_space i_data; 设备地址映射
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS]; 节点的磁盘限额
#endif
struct list_head i_devices; 块设备链表
union {
struct pipe_inode_info *i_pipe; 这个是网络设备? 管道信息
struct block_device *i_bdev; 块设备驱动
struct cdev *i_cdev; 当inode指向一个字符设备文件时
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif
#ifdef CONFIG_INOTIFY
struct list_head inotify_watches; /* watches on this inode */
struct mutex inotify_mutex; /* protects the watches list */
#endif
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned int i_flags;
atomic_t i_writecount;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};
* 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;
}
file_permis sion - -> inode_permission(file->f_path.dentry->d_inode, mask);
int inode_permission(struct inode *inode, int mask)然后又是kernel_ read到底是读到哪里啊,读文件还是读内存啊。读到bprm->buf里吧。
{
int retval;
if (mask & MAY_WRITE) {
umode_t mode = inode->i_mode;
/*
* Nobody gets write access to a read-only fs.
*/
if (IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/*
* Nobody gets write access to an immutable file.
*/
if (IS_IMMUTABLE(inode))
return -EACCES;
}
if (inode->i_op->permission)
retval = inode->i_op->permission(inode, mask);
else
retval = generic_permission(inode, mask, inode->i_op->check_acl);
if (retval)
return retval;
retval = devcgroup_inode_permission(inode, mask);
if (retval)
return retval;
return security_inode_permission(inode,
mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
}好像在确认文件的读写执行的权限,具体的也不是很看得懂了
通过kernel_read()读入其开头128个字节,这就是映像的头部。
/* Get the exec headers */
loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
break;
}
elf_ppnt++;
}
又获取了头部,这么翻来覆去的搞,指是把东西读出来么,循环到这里结束了。
读出 来判断是否有动态 链 接库?
elf_ppnt = elf_phdata; 为什么又赋一遍,上面的循环elf_ppnt已经到底了,又让它回来了
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;
}
又检查是否是可执 行文件, elf_interpreter怎么是动态链接器 呢
/* Flush all traces of the currently running executable */
retval = flush_old_exec(bprm);
if (retval)
goto out_free_dentry;
flush_old_exec()把当前进程用户空间的页面都释放了
/* 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;
current->flags |= PF_RANDOMIZE;
setup_new_exec(bprm);
这个函数完全不能理解
void setup_new_exec(struct linux_binprm * bprm)
{
int i, ch;
char * name;
char tcomm[sizeof(current->comm)];
arch_pick_mmap_layout(current->mm); 好像在判断栈的格式
/* This is the point of no return */
current->sas_ss_sp = current->sas_ss_size = 0;
if (current_euid() == current_uid() && current_egid() == current_gid())
set_dumpable(current->mm, 1);
else
set_dumpable(current->mm, suid_dumpable);
name = bprm->filename;
/* Copies the binary name from after last slash */
for (i=0; (ch = *(name++)) != '\0';) {
if (ch == '/')
i = 0; /* overwrite what we wrote */
else
if (i < (sizeof(tcomm) - 1))
tcomm[i++] = ch;
}
tcomm[i] = '\0';
set_task_comm(current, tcomm);
/* Set the new mm task size. We have to do that late because it may
* depend on TIF_32BIT which is only updated in flush_thread() on
* some architectures like powerpc
*/
current->mm->task_size = TASK_SIZE;
/* install the new credentials */
if (bprm->cred->uid != current_euid() ||
bprm->cred->gid != current_egid()) {
current->pdeath_signal = 0;
} else if (file_permission(bprm->file, MAY_READ) ||
bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP) {
set_dumpable(current->mm, suid_dumpable);
}
/ *
* Flush performance counters when crossing a
* security domain:
*/
if (!get_dumpable(current->mm))
perf_event_exit_task(current);
/* An exec changes our domain. We are no longer part of the thread
group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
flush_old_files(current->files);
}
/* 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 mmapping 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; mmap的flag?
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) { 如果为共享库,要加偏移量,e_type 为3就是共享库
/* 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
}
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不是本来就是0吗
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)
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; 段虚拟地址加上段大小就到了段尾
if (k > elf_bss)
elf_bss = k; 后面接elf_bss?
if ((elf_ppnt->p_flags & PF_X) && end_code < k) ELF 段标志可执行 并且。。。
end_code = k;
if (end_data < k)
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_bi
相关热门文章
给主人留下些什么吧!~~
评论热议