Chinaunix首页 | 论坛 | 博客
  • 博客访问: 361762
  • 博文数量: 64
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 831
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-14 10:59
文章存档

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-04-30 14:14:45

ld.so分析2

内核是如何执行程序的,本分析基于内核版本2.4.0

1.用户空间接口
man execve显示如下的函数原型

       execve - execute program

SYNOPSIS
       #include

       int  execve(const  char  *filename,  char  *const  argv [], char *const
       envp[]);

2.glibc中实现

在glibc中,execve对应的文件是
sysdeps/unix/sysv/linux/execve.c

int
__execve (file, argv, envp)
     const char *file;
     char *const argv[];
     char *const envp[];
{
  /* If this is a threaded application kill all other threads.  */
  if (__pthread_kill_other_threads_np)
    __pthread_kill_other_threads_np ();
#if __BOUNDED_POINTERS__ //该宏未定义
  {
    char *const *v;
    int i;
    char *__unbounded *__unbounded ubp_argv;
    char *__unbounded *__unbounded ubp_envp;
    char *__unbounded *__unbounded ubp_v;

    for (v = argv; *v; v++)
      ;
    i = v - argv + 1;
    ubp_argv = (char *__unbounded *__unbounded) alloca (sizeof (*ubp_argv) * i);
    for (v = argv, ubp_v = ubp_argv; --i; v++, ubp_v++)
      *ubp_v = CHECK_STRING (*v);
    *ubp_v = 0;

    for (v = envp; *v; v++)
      ;
    i = v - envp + 1;
    ubp_envp = (char *__unbounded *__unbounded) alloca (sizeof (*ubp_envp) * i);
    for (v = envp, ubp_v = ubp_envp; --i; v++, ubp_v++)
      *ubp_v = CHECK_STRING (*v);
    *ubp_v = 0;

    return INLINE_SYSCALL (execve, 3, CHECK_STRING (file), ubp_argv, ubp_envp);
  }
#else
  return INLINE_SYSCALL (execve, 3, file, argv, envp);//所以这行有效
#endif
}

INLINE_SYSCALL的定义在
sysdeps/unix/sysv/linux/i386/sysdeps.h

#define INLINE_SYSCALL(name, nr, args...) \
  ({                                          \
    unsigned int resultvar;                              \
    asm volatile (                                  \
    LOADARGS_##nr                                  \
    "movl %1, %%eax\n\t"                              \
    "int $0x80\n\t"                                  \
    RESTOREARGS_##nr                                  \
    : "=a" (resultvar)                                  \
    : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc");              \
    if (resultvar >= 0xfffff001)                          \
      {                                          \
    __set_errno (-resultvar);                          \
    resultvar = 0xffffffff;                              \
      }                                          \
    (int) resultvar; })

3.手工展开看看

  ({                                          
    unsigned int resultvar;                              
    asm volatile (                                  
    LOADARGS_3                                  
    "movl %1, %%eax\n\t"                              
    "int $0x80\n\t"                                  
    RESTOREARGS_3                                  
    : "=a" (resultvar)                                  
    : "i" (__NR_execve) ASMFMT_3(args) : "memory", "cc");              
    if (resultvar >= 0xfffff001)                          
      {                                          
    __set_errno (-resultvar);                          
    resultvar = 0xffffffff;                              
      }                                          
    (int) resultvar; })

其中__NR_execve是execve的系统调用号,为11,定义在头文件unistd.h中
这其中又涉及到三个宏

#define LOADARGS_1 \
    "bpushl .L__X'%k2, %k2\n\t"                              \
    "bmovl .L__X'%k2, %k2\n\t"
#define LOADARGS_3    LOADARGS_1

#define RESTOREARGS_1 \
    "bpopl .L__X'%k2, %k2\n\t"
#define RESTOREARGS_3    RESTOREARGS_1

#define ASMFMT_3(arg1, arg2, arg3) \
    , "aCD" (arg1), "c" (arg2), "d" (arg3)

展开
  ({                                          
    unsigned int resultvar;                              
    asm volatile (                                  
    "bpushl .L__X'%k2, %k2\n\t"                              
    "bmovl .L__X'%k2, %k2\n\t"    
    "movl %1, %%eax\n\t"                              
    "int $0x80\n\t"                                  
    "bpopl .L__X'%k2, %k2\n\t"
    : "=a" (resultvar)                                  
    : "i" (11) , "aCD" (arg1), "c" (arg2), "d" (arg3) : "memory", "cc");              
    if (resultvar >= 0xfffff001)                          
      {                                          
    __set_errno (-resultvar);                          
    resultvar = 0xffffffff;                              
      }                                          
    (int) resultvar; })

这里又涉及到三个asm宏,bpushl,bmovl,bpopl
定义如下(也在该文件sysdeps.h中)
asm (".L__X'%ebx = 1\n\t"
     ".L__X'%ecx = 2\n\t"
     ".L__X'%edx = 2\n\t"
     ".L__X'%eax = 3\n\t"
     ".L__X'%esi = 3\n\t"
     ".L__X'%edi = 3\n\t"
     ".L__X'%ebp = 3\n\t"
     ".L__X'%esp = 3\n\t"
     ".macro bpushl name reg\n\t"
     ".if 1 - \\name\n\t"
     ".if 2 - \\name\n\t"
     "pushl %ebx\n\t"
     ".else\n\t"
     "xchgl \\reg, %ebx\n\t"
     ".endif\n\t"
     ".endif\n\t"
     ".endm\n\t"
     ".macro bpopl name reg\n\t"
     ".if 1 - \\name\n\t"
     ".if 2 - \\name\n\t"
     "popl %ebx\n\t"
     ".else\n\t"
     "xchgl \\reg, %ebx\n\t"
     ".endif\n\t"
     ".endif\n\t"
     ".endm\n\t"
     ".macro bmovl name reg\n\t"
     ".if 1 - \\name\n\t"
     ".if 2 - \\name\n\t"
     "movl \\reg, %ebx\n\t"
     ".endif\n\t"
     ".endif\n\t"
     ".endm\n\t");

根据约束条件
%eax分配给resultvar
%ecx分配给argv
%edx分配给envp
则约束条件"aCD"中,a(%eax)已分配,C无效,因此分配%edi给file

手工展开
    mov     file,%edi
    mov     argv,%ecx
    mov     envp,%edx
    bpushl     .L__X'%edi, %edi
    bmovl     .L__X'·%edi, %%edi
    movl     11, %%eax
    int     $0x80
    bpopl     .L__X'%edi, %edi

手工展开
    mov     file,%edi
    mov     argv,%ecx
    mov     envp,%edx
    .if 1 - .L_X'%edi
    .if 2 - .L_X'%edi
    pushl %ebx
    .else
    xchgl %edi, %ebx
    .endif
    .endif
    .if 1 - .L_X'%edi
    .if 2 - .L_X'%edi
    movl %edi, %ebx
    .endif
    .endif
    movl     11, %%eax
    int     $0x80
    .if 1 - .L_X'%edi
    .if 2 - .L_X'%edi
    popl %ebx
    .else
    xchgl %edi, %ebx
    .endif
    .endif

由于L__X'%edi = 3,展开

    mov     file,%edi
    mov     argv,%ecx
    mov     envp,%edx
    .if 1 - 3
    .if 2 - 3
    pushl %ebx
    .else
    xchgl %edi, %ebx
    .endif
    .endif
    .if 1 - 3
    .if 2 - 3
    movl %edi, %ebx
    .endif
    .endif
    movl     11, %%eax
    int     $0x80
    .if 1 - 3
    .if 2 - 3
    popl %ebx
    .else
    xchgl %edi, %ebx
    .endif
    .endif

.if为真的条件是不等于0,展开

    mov     file,%edi
    mov     argv,%ecx
    mov     envp,%edx
    pushl %ebx
    movl %edi, %ebx
    movl     11, %%eax
    int     $0x80
    popl %ebx


最终编译结果是
mov    0x8(%ebp),%edi
mov    0xc(%ebp),%ecx
mov    0x10(%ebp),%edx
push   %ebx
mov    %edi,%ebx
mov    $0xb,%eax
int    $0x80
pop    %ebx

正好一致

系统调用传参使用%ebx,%ecx,%edx,%esi,%edi这五个寄存器,因此最多只能传五个参数.

4.返回值的处理
#  define __set_errno(val) (*__errno_location ()) = (val)

    if (resultvar >= 0xfffff001)//如果返回值>=0xfffff001,则出错                          
      {                                          
    __set_errno (-resultvar);// 预处理时被替换成(*__errno_location ()) = (-resultvar);设置errno为-resultvar          
    resultvar = 0xffffffff;    //-1                          
      }


__errno_location的定义是
sysdeps/generic/errno-loc.c

int * __errno_location (void)
{
  return &errno;
}


5.也可使用如下宏生成调用系统调用execve的代码
linux/include/asm-i386/unistd.h

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
          "d" ((long)(arg3))); \
__syscall_return(type,__res); \
}

例如
_syscall3(int,execve,const char *,file,char *const,argv[],char *const,envp[])
能生成和glibc相似的代码


6.sys_execve
linux/arch/i386/kernel/process.c

/*
 * sys_execve() executes a new program.
 */
asmlinkage int sys_execve(struct pt_regs regs)
{
    int error;
    char * filename;

    filename = getname((char *) regs.ebx);
    error = PTR_ERR(filename);
    if (IS_ERR(filename))
        goto out;
    //do_execve成功替换掉执行影像后,在返回到用户空间时,执行权才交给新的影像
    error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, ®s);
    if (error == 0)
        current->ptrace &= ~PT_DTRACE;//取消单步跟踪
    putname(filename);
out:
    return error;
}

7.do_execve(sys_execve->do_execve)
fs/exec.c

/*
 * sys_execve() executes a new program.
 */
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
{
    struct linux_binprm bprm;
    struct file *file;
    int retval;
    int i;

    file = open_exec(filename);

    retval = PTR_ERR(file);
    if (IS_ERR(file))
        return retval;

    bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);//参数最多占32个页面,最后一个字存放NULL
    memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));//清空页指针

    bprm.file = file;
    bprm.filename = filename;
    bprm.sh_bang = 0;
    bprm.loader = 0;
    bprm.exec = 0;
    //计算argv数组的长度,该数组是0结束
    if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
        allow_write_access(file);
        fput(file);
        return bprm.argc;
    }

    //计算envp数组的长度
    if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
        allow_write_access(file);
        fput(file);
        return bprm.envc;
    }

    retval = prepare_binprm(&bprm);
    if (retval < 0)
        goto out;

    retval = copy_strings_kernel(1, &bprm.filename, &bprm);//复制文件名
    if (retval < 0)
        goto out;

    bprm.exec = bprm.p;
    retval = copy_strings(bprm.envc, envp, &bprm);//复制envp
    if (retval < 0)
        goto out;

    retval = copy_strings(bprm.argc, argv, &bprm);//复制argv
    if (retval < 0)
        goto out;

    retval = search_binary_handler(&bprm,regs);
    if (retval >= 0)
        /* execve success */
        return retval;

out:
    /* Something went wrong, return the inode and free the argument pages*/
    allow_write_access(bprm.file);
    if (bprm.file)
        fput(bprm.file);

    for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
        struct page * page = bprm.page[i];
        if (page)
            __free_page(page);
    }

    return retval;
}

8.copy_strings(sys_execve->do_execve->copy_strings)
fs/exec.c

/*
 * 'copy_strings()' copies argument/envelope strings from user
 * memory to free pages in kernel mem. These are in a format ready
 * to be put directly into the top of new user memory.
 */
 //从用户空间拷贝数据到空闲页
int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
{
    while (argc-- > 0) {//argc--
        char *str;
        int len;
        unsigned long pos;
        //上面argc--
        if (get_user(str, argv+argc) || !str || !(len = strnlen_user(str, bprm->p)))
            return -EFAULT;
        if (bprm->p < len) //空间不够
            return -E2BIG;

        bprm->p -= len;//从后往前考
        /* XXX: add architecture specific overflow check here. */

        pos = bprm->p;
        while (len > 0) {
            char *kaddr;
            int i, new, err;
            struct page *page;
            int offset, bytes_to_copy;

            offset = pos % PAGE_SIZE;//页内偏移
            i = pos/PAGE_SIZE;//页号
            page = bprm->page[i];
            new = 0;
            if (!page) {
                page = alloc_page(GFP_HIGHUSER);
                bprm->page[i] = page;
                if (!page)
                    return -ENOMEM;
                new = 1;
            }
            kaddr = kmap(page);

            if (new && offset)//是新页,offset>0,清[0,offset)
                memset(kaddr, 0, offset);
            bytes_to_copy = PAGE_SIZE - offset;
            if (bytes_to_copy > len) {
                bytes_to_copy = len;
                if (new)//清[offset+len,PAGE_SIZE)
                    memset(kaddr+offset+len, 0, PAGE_SIZE-offset-len);
            }
            err = copy_from_user(kaddr + offset, str, bytes_to_copy);
            kunmap(page);

            if (err)
                return -EFAULT;

            pos += bytes_to_copy;//可能跨页
            str += bytes_to_copy;
            len -= bytes_to_copy;
        }
    }
    return 0;
}

执行到这里bprm->p内存空间布局如下
                    [ argument ASCIIZ strings ]   >= 0
                    [ environment ASCIIZ str. ]   >= 0
                    [filename]
  (0xbffffffc)      [ end marker ]                4   (= NULL)

  (0xc0000000)      < top of stack >              0   (virtual)

写一个程序验证一下
系统redhat7.2
[root@proxy ~]# uname -a
Linux proxy 2.4.7-10smp #1 SMP Thu Sep 6 17:09:31 EDT 2001 i686 unknown
[root@proxy ~]#
root@proxy ~]# cat 1.c
#include

int main(int argc,char * argv[],char * envp[])
{
unsigned char * p;
printf("%d,%p,%p\n",argc,argv,envp);
p=(unsigned char *)argv;
for(;p<(unsigned char *)0xc0000000;p++)
if(isprint(*p))
        printf("%c",*p);
else
        printf("\\%x",*p);
return 0;
}

[root@proxy ~]# ./a.out
1,0xbffffb04,0xbffffb0c
\3\fc\ff\bf\0\0\0\0\b\fc\ff\bf\15\fc\ff\bf$\fc\ff\bf<\fc\ff\bf^\fc\ff\bfj\fc\ff\bft\fc\ff\bf7\fe\ff\bfV\fe\ff\bfp\fe\ff\bf\85\fe\ff\bf\9c\fe\ff\bf\a7\fe\ff\bf\b4\fe\ff\bf\bc\fe\ff\bf\cc\fe\ff\bf\da\fe\ff\bf\e8\fe\ff\bf\f9\fe\ff\bf\7\ff\ff\bf\12\ff\ff\bf\1d\ff\ff\bfI\ff\ff\bf|\ff\ff\bf\d7\ff\ff\bf\ea\ff\ff\bf\0\0\0\0\10\0\0\0\ff\fb\83\3\6\0\0\0\0\10\0\0\11\0\0\0d\0\0\0\3\0\0\04\80\4\8\4\0\0\0 \0\0\0\5\0\0\0\6\0\0\0\7\0\0\0\0\0\0@\8\0\0\0\0\0\0\0\9\0\0\0\90\83\4\8\b\0\0\0\0\0\0\0\c\0\0\0\0\0\0\0\d\0\0\0\0\0\0\0\e\0\0\0\0\0\0\0\f\0\0\0\fe\fb\ff\bf\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0i686\0./a.out\0PWD=/root\0HOSTNAME=proxy\0QTDIR=/usr/lib/qt-2.3.1\0LESSOPEN=|/usr/bin/lesspipe.sh %s\0KDEDIR=/usr\0USER=root\0LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:\0MACHTYPE=i386-redhat-linux-gnu\0MAIL=/var/spool/mail/root\0INPUTRC=/etc/inputrc\0BASH_ENV=/root/.bashrc\0LANG=en_US\0LOGNAME=root\0SHLVL=1\0SHELL=/bin/bash\0USERNAME=root\0HOSTTYPE=i386\0OSTYPE=linux-gnu\0HISTSIZE=1000\0HOME=/root\0TERM=linux\0SSH_AUTH_SOCK=/tmp/ssh-XXi40Qtw/agent.23262\0SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass\0PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin\0SSH_TTY=/dev/pts/0\0_=./a.out\0./a.out\0\0\0\0\0



9.search_binary_handler(sys_execve->do_execve->search_binary_handler)
fs/exec.c

/*
 * cycle the list of binary formats handler, until one recognizes the image
 */
int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
{
    int try,retval=0;
    struct linux_binfmt *fmt;
#ifdef __alpha__
    /* handle /sbin/loader.. */
    {
        struct exec * eh = (struct exec *) bprm->buf;

        if (!bprm->loader && eh->fh.f_magic == 0x183 &&
        (eh->fh.f_flags & 0x3000) == 0x3000)
        {
        char * dynloader[] = { "/sbin/loader" };
        struct file * file;
        unsigned long loader;

        allow_write_access(bprm->file);
        fput(bprm->file);
        bprm->file = NULL;

            loader = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);

        file = open_exec(dynloader[0]);
        retval = PTR_ERR(file);
        if (IS_ERR(file))
            return retval;
        bprm->file = file;
        bprm->loader = loader;
        retval = prepare_binprm(bprm);
        if (retval<0)
            return retval;
        /* should call search_binary_handler recursively here,
           but it does not matter */
        }
    }
#endif
    for (try=0; try<2; try++) {
        read_lock(&binfmt_lock);
        for (fmt = formats ; fmt ; fmt = fmt->next) {
            int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
            if (!fn)
                continue;
            if (!try_inc_mod_count(fmt->module))
                continue;
            read_unlock(&binfmt_lock);
            retval = fn(bprm, regs);//调用该文件格式的load_binary
            if (retval >= 0) {//成功
                put_binfmt(fmt);
                allow_write_access(bprm->file);//allow write
                if (bprm->file)
                    fput(bprm->file);
                bprm->file = NULL;
                current->did_exec = 1;//可以执行了
                return retval;
            }
            read_lock(&binfmt_lock);
            put_binfmt(fmt);
            if (retval != -ENOEXEC)
                break;
            if (!bprm->file) {
                read_unlock(&binfmt_lock);
                return retval;
            }
        }
        read_unlock(&binfmt_lock);
        if (retval != -ENOEXEC) {
            break;
#ifdef CONFIG_KMOD
        }else{
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
            char modname[20];
            if (printable(bprm->buf[0]) &&
                printable(bprm->buf[1]) &&
                printable(bprm->buf[2]) &&
                printable(bprm->buf[3]))
                break; /* -ENOEXEC 不允许都是可打印字符*/
            sprintf(modname, "binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
            request_module(modname);
#endif
        }
    }
    return retval;
}

elf文件的相关处理结构在fs/binfmt_elf.c中

static int __init init_elf_binfmt(void)
{
    return register_binfmt(&elf_format);
}

static struct linux_binfmt elf_format = {
    NULL, THIS_MODULE, load_elf_binary, load_elf_library, elf_core_dump, ELF_EXEC_PAGESIZE
};

因此elf的load_binary函数是load_elf_binary

阅读(1889) | 评论(0) | 转发(0) |
0

上一篇:ld.so分析1

下一篇:ld.so分析3

给主人留下些什么吧!~~