Linux内核分析-7/程序的装载(基于fork)

来源:互联网 发布:源码资本投资的项目 编辑:程序博客网 时间:2024/06/07 08:08

程序的装载(基于fork)

  • 上篇博客说了fork的东西
  • 下面我们要使用 fork 联合 exec 族函数来做一次程序的装载
  • 我们知道fork就是复制了一个进程块(设置了ebp esp eip),并且加入了进程调度中.
  • 然后exec函数改掉了进程块(修改了ebp esp eip).

//假如一个函数中 fork 了,并 exec 了一个程序.fork我们已经知道,exec 也是一种系统调用.//另外还有在main.c的mian函数中init函数中的execv(sh),这是一个经典的fork exec 实例

linux0.11中的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 ; i<MAX_ARG_PAGES ; i++)   /* clear page-table */        page[i]=0;    if (!(inode=namei(filename)))       /* get executables inode */        return -ENOENT;    argc = count(argv);    envc = count(envp);restart_interp:    if (!S_ISREG(inode->i_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<NR_OPEN ; i++)        if ((current->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<MAX_ARG_PAGES ; i++)        free_page(page[i]);    return(retval);}

do_execve函数的流程


下面的说的是glibc-2.25和linux-3.10中的调用路径


glibc路径

//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

linux3.10路径

//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
阅读全文
0 0
原创粉丝点击