linux ld so 源码分析,ld.so分析2

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值