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, &regs);

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(;pif(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

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

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

linux ld so 源码分析,ld.so分析2相关推荐

  1. Linux PPP实现源码分析

    Linux PPP实现源码分析 作者:kwest <exboy@163.com>  版本:v0.7 ©所有版权保留 转载请保留作者署名,严禁用于商业用途 . 前言: PPP(Point t ...

  2. Linux PPP 实现源码分析

    nux PPP实现源码分析 2013-05-21 23:44  6091人阅读  评论(18)  收藏  举报   分类: Linux 版权声明:本文为博主原创文章,未经博主允许不得转载. Linux ...

  3. Linux内核学习(五):linux kernel源码结构以及makefile分析

    Linux内核学习(五):linux kernel源码结构以及makefile分析 前面我们知道了linux内核镜像的生成.加载以及加载工具uboot. 这里我们来看看linux内核的源码的宏观东西, ...

  4. 第十期-Linux内核补丁源码分析(2)

    作者:罗宇哲,中国科学院软件研究所智能软件研究中心 在上一期中,我们通过CAKE系统的实例介绍了一种对Linux内核补丁的初步分析方法,这一期我们将继续通过CAKE系统的例子介绍一种对补丁文件源码的分 ...

  5. linux网卡驱动源码分析

    转自http://blog.csdn.net/ustc_dylan/article/details/6329375 网络驱动是一种典型的PCI设备驱动,无论在嵌入式平台还是在PC领域,网络相关的项目开 ...

  6. linux网卡驱动源码分析(一)

    linux网卡驱动源码分析(一) linux struct linux内核 网络 descriptor resources 转自http://blog.csdn.net/ustc_dylan/arti ...

  7. linux打印源码,Linux打印机驱动源码分析.pdf

    您所在位置:网站首页 > 海量文档 &nbsp>&nbsp计算机&nbsp>&nbsplinux/Unix相关 Linux打印机驱动源码分析.pdf1 ...

  8. Linux驱动入门(三)——源码下载阅读、分析和嵌入式文件系统介绍

    文章目录 从内核出发 获取内核源码 使用Git 安装内核源码 使用补丁 阅读Linux内核源码 Source Insight简介 阅读源码 内核开发的特点 无libc库抑或无标准头文件 GNU C 没 ...

  9. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  10. Colly源码解析——结合例子分析底层实现

    通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...

最新文章

  1. (C++)数组作为函数参数
  2. Log probability 维基百科
  3. laravel 重写以及500错误
  4. win7下安装Oracle10g解决方案
  5. 设计模式--23、访问者模式
  6. pip与conda简述
  7. Apollo 在Windows下安装使用
  8. 第一章 进程与线程的基本概念
  9. 战神背光键盘如何关系_苹果新专利:未来键盘或用彩色背光向用户提供有用的反馈...
  10. Mapx中的图元移动
  11. 纽微特反省:别人犯错不敢说,那是因为自己不干活
  12. 谷歌、bing 翻译插件调研总结
  13. [UVM]UVM Register Test Sequence
  14. yourshelf是什么意思中文_[英语shelf的中文是什么意思]英语shelf的中文是什么意思...
  15. 自动升级Notes客户机AUT功能实战
  16. 查看 设置mysql时区
  17. 2dpca matlab程序,[转载]人脸识别-2dpca之Matlab程序
  18. Unity官方案例——Roll a ball
  19. 1.3 eclips下载与安装
  20. [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever...

热门文章

  1. PythonScript_003_通过Cookie模拟登陆人人网
  2. docker+ftp+openldap记录接入openldap过程 配置
  3. iOS中延时执行(睡眠)的几种方法
  4. 从“黑五”看亚马逊海外购的变与不变
  5. 【技术贴】解决前台js传参中文乱码
  6. 巨高兴,自己的“万能数据库查询分析器”中英文 3.01版本 已经在国内6大软件下载网站发布
  7. 精选“数据分析”好问题汇总·第一期
  8. 哪种锻炼方式最能让程序猿远离亚健康? - 强烈推荐
  9. 2021数学建模国赛B题复盘详细解析
  10. 【CV系列】图像算法之一:Randon变换