本文共 8230 字,大约阅读时间需要 27 分钟。
//假如一个函数中 fork 了,并 exec 了一个程序.fork我们已经知道,exec 也是一种系统调用.//另外还有在main.c的mian函数中init函数中的execv(sh),这是一个经典的fork exec 实例
//1/上层//execve(lib/execve.c)_syscall3(int,execve,const char *,file,char **,argv,char **,envp)#define _syscall3(type,name,atype,a,btype,b,ctype,c) \type name(atype a,btype b,ctype c) \{ \long __res; \__asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \if (__res>=0) \ return (type) __res; \errno=-__res; \return -1; \}//2/system_call(system_call.s)//3/sys_execve(system_call.s).align 2_sys_execve: lea EIP(%esp),%eax pushl %eax call _do_execve addl $4,%esp ret//do_execve(fs/exec.c)/* * 'do_execve()' executes a new program. */int do_execve(unsigned long * eip,long tmp,char * filename, char ** argv, char ** envp){ struct m_inode * inode; struct buffer_head * bh; struct exec ex; unsigned long page[MAX_ARG_PAGES]; int i,argc,envc; int e_uid, e_gid; int retval; int sh_bang = 0; unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4; if ((0xffff & eip[1]) != 0x000f) panic("execve called from supervisor mode"); for (i=0 ; ii_mode)) { /* must be regular file */ retval = -EACCES; goto exec_error2; } i = inode->i_mode; e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; if (current->euid == inode->i_uid) i >>= 6; else if (current->egid == inode->i_gid) i >>= 3; if (!(i & 1) && !((inode->i_mode & 0111) && suser())) { retval = -ENOEXEC; goto exec_error2; } if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { retval = -EACCES; goto exec_error2; } ex = *((struct exec *) bh->b_data); /* read exec-header */ if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) { /* * This section does the #! interpretation. * Sorta complicated, but hopefully it will work. -TYT */ char buf[1023], *cp, *interp, *i_name, *i_arg; unsigned long old_fs; strncpy(buf, bh->b_data+2, 1022); brelse(bh); iput(inode); buf[1022] = '\0'; if (cp = strchr(buf, '\n')) { *cp = '\0'; for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++); } if (!cp || *cp == '\0') { retval = -ENOEXEC; /* No interpreter name found */ goto exec_error1; } interp = i_name = cp; i_arg = 0; for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { if (*cp == '/') i_name = cp+1; } if (*cp) { *cp++ = '\0'; i_arg = cp; } /* * OK, we've parsed out the interpreter name and * (optional) argument. */ if (sh_bang++ == 0) { p = copy_strings(envc, envp, page, p, 0); p = copy_strings(--argc, argv+1, page, p, 0); } /* * Splice in (1) the interpreter's name for argv[0] * (2) (optional) argument to interpreter * (3) filename of shell script * * This is done in reverse order, because of how the * user environment and arguments are stored. */ p = copy_strings(1, &filename, page, p, 1); argc++; if (i_arg) { p = copy_strings(1, &i_arg, page, p, 2); argc++; } p = copy_strings(1, &i_name, page, p, 2); argc++; if (!p) { retval = -ENOMEM; goto exec_error1; } /* * OK, now restart the process with the interpreter's inode. */ old_fs = get_fs(); set_fs(get_ds()); if (!(inode=namei(interp))) { /* get executables inode */ set_fs(old_fs); retval = -ENOENT; goto exec_error1; } set_fs(old_fs); goto restart_interp; } brelse(bh); if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || ex.a_text+ex.a_data+ex.a_bss>0x3000000 || inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { retval = -ENOEXEC; goto exec_error2; } if (N_TXTOFF(ex) != BLOCK_SIZE) { printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); retval = -ENOEXEC; goto exec_error2; } if (!sh_bang) { p = copy_strings(envc,envp,page,p,0); p = copy_strings(argc,argv,page,p,0); if (!p) { retval = -ENOMEM; goto exec_error2; } }/* OK, This is the point of no return */ if (current->executable) iput(current->executable); current->executable = inode; for (i=0 ; i<32 ; i++) current->sigaction[i].sa_handler = NULL; for (i=0 ; i close_on_exec>>i)&1) sys_close(i); current->close_on_exec = 0; free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); if (last_task_used_math == current) last_task_used_math = NULL; current->used_math = 0; p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE; p = (unsigned long) create_tables((char *)p,argc,envc); current->brk = ex.a_bss + (current->end_data = ex.a_data + (current->end_code = ex.a_text)); current->start_stack = p & 0xfffff000; current->euid = e_uid; current->egid = e_gid; i = ex.a_text+ex.a_data; while (i&0xfff) put_fs_byte(0,(char *) (i++)); eip[0] = ex.a_entry; /* eip, magic happens :-) */ eip[3] = p; /* stack pointer */ return 0;exec_error2: iput(inode);exec_error1: for (i=0 ; i
//execle.cintexecle (const char *path, const char *arg, ...){ ptrdiff_t argc; va_list ap; va_start (ap, arg); for (argc = 1; va_arg (ap, const char *); argc++) { if (argc == INT_MAX) { va_end (ap); errno = E2BIG; return -1; } } va_end (ap); /* Avoid dynamic memory allocation due two main issues: 1. The function should be async-signal-safe and a running on a signal handler with a fail outcome might lead to malloc bad state. 2. It might be used in a vfork/clone(VFORK) scenario where using malloc also might lead to internal bad state. */ ptrdiff_t i; char *argv[argc + 1]; char **envp; va_start (ap, arg); argv[0] = (char *) arg; for (i = 1; i <= argc; i++) argv[i] = va_arg (ap, char *); envp = va_arg (ap, char **); va_end (ap); return __execve (path, argv, envp);}libc_hidden_def (execle)//execle.c/* Replace the current process, executing PATH with arguments ARGV and environment ENVP. ARGV and ENVP are terminated by NULL pointers. */int__execve (const char *path, char *const argv[], char *const envp[]){ if (path == NULL || argv == NULL || envp == NULL) { __set_errno (EINVAL); return -1; } __set_errno (ENOSYS); return -1;}stub_warning (execve)weak_alias (__execve, execve)//glibc没有找到 int 80
//do_execve_common$ grep SYSCALL_DEFINE * -nr |grep execfs/exec.c:105:SYSCALL_DEFINE1(uselib, const char __user *, library)fs/exec.c:1677:SYSCALL_DEFINE3(execve,kernel/kexec.c:935:SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,kernel/exec_domain.c:182:SYSCALL_DEFINE1(personality, unsigned int, personality)//fs/exec.cSYSCALL_DEFINE3(execve, const char __user *, filename, const char __user *const __user *, argv, const char __user *const __user *, envp){ struct filename *path = getname(filename); int error = PTR_ERR(path); if (!IS_ERR(path)) { error = do_execve(path->name, argv, envp); putname(path); } return error;}//fs/exec.cint 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);}//fs/exec.cdo_execve_common