linux系统调用execve

exec系列函数主要实现装入新的可执行文件或脚本镜像,并执行;调用后不再返回,而是跳转到新镜像的入口去执行。
exec在linux上均是对execve系统调用的封装,除了下述内容外,进程其它内容均保持不变:
1.将设置了处理函数的信号handler,重置为默认SIG_DFL
2.内存映射,mmap
3.SysV共享内存,shmat
4.POSIX共享内存,shm_open
5.POSIX消息队列,mq_overview
6.POSIX信号量,sem_overview
7.打开的目录,open_dir
8.内存锁,mlock/mlockall
9.exit处理,atexit/on_exit
10.浮点数环境,fenv


execve的主要作用为:
1.分配进程新的地址空间,将环境变量、main参数等拷贝到新地址空间的推栈中;
2.解析可执行文件,将代码、数据装入/映射到内存
3.进程新环境的设置,如关闭设置FD_CLOEXEC的文件等
4.设置execve函数返回到用户态时的执行地址;解析器入口地址或程序的入口地址

I.do_execve
execve系统调用是通过do_execve实现的,其处理流程如下:
1.进程分配并使用自己的文件描述符表,不再使用共享的文件描述符表
2.分配进程新的地址空间mm_struct,并复制老mm_struct的context,并分配一页大小的堆栈内存区。
3.打开可执行文件;检查可执行文件权限,并预读可执行文件的前BINPRM_BUF_SIZE=128字节数据到buf中
4.计算环境变量、main参数个数;并将这些参数复制到新地址空间的堆栈中
5.根据可执行文件的格式,查找对应的handler作后续处理;

do_execve的实现fs/exec.c:

1342 /*
1343  * sys_execve() executes a new program.
1344  */
1345 int do_execve(char * filename,
1346         char __user *__user *argv,
1347         char __user *__user *envp,
1348         struct pt_regs * regs)
1349 {

1355 
1356         retval = unshare_files(&displaced);
1357         if (retval)
1358                 goto out_ret;
1359 

1375         file = open_exec(filename);
1376         retval = PTR_ERR(file);
1377         if (IS_ERR(file))
1378                 goto out_unmark;
1379 

1385 
1386         retval = bprm_mm_init(bprm);
1387         if (retval)
1388                 goto out_file;
1389 
1390         bprm->argc = count(argv, MAX_ARG_STRINGS);
1391         if ((retval = bprm->argc) < 0)
1392                 goto out;
1393 
1394         bprm->envc = count(envp, MAX_ARG_STRINGS);
1395         if ((retval = bprm->envc) < 0)
1396                 goto out;
1397 
1398         retval = prepare_binprm(bprm);
1399         if (retval < 0)
1400                 goto out;
1401 
1402         retval = copy_strings_kernel(1, &bprm->filename, bprm);
1403         if (retval < 0)
1404                 goto out;
1405 
1406         bprm->exec = bprm->p;
1407         retval = copy_strings(bprm->envc, envp, bprm);
1408         if (retval < 0)
1409                 goto out;
1410 
1411         retval = copy_strings(bprm->argc, argv, bprm);
1412         if (retval < 0)
1413                 goto out;
1414 
1415         current->flags &= ~PF_KTHREAD;
1416         retval = search_binary_handler(bprm,regs);
1417         if (retval < 0)
1418                 goto out;
1419 

1453         return retval;
1454 }

字符串复制copy_strings函数:
1、将字符串从当前地址空间复制到新地址空间的堆栈中;
2、复制字符串时,使用的仍是老地址空间页表,所以不能使用新地址空间的堆栈地址直接复制,必须映射堆栈页帧到内核空间后再做复制操作。
3、由于字符串可能跨页,一个字符串可能存在两次页帧映射与复制

 

II.ELF格式文件处理
ELF格式文件处理主要对ELF文件进行解析,将可执行程序的代码及数据装载到内存中;并根据ELF文件信息,设置进程的环境

ELF格式:

ELF的管理信息主要由三部分组成:ELF头、程序头(程序段)表、节区表;一个段的地址空间通常包括一个或多个节区。
如数据段包括数据节区、BSS节区等

linux对ELF格式文件处理代码为load_elf_binary,主要流程如下:
i.根据ELF文件头数据,检查ELF文件的合法性
ii.根据ELF文件头中程序头表偏移和程序头数目,读出程序头表数据。
iii.根据解析器程序头信息,读出解析器可执行文件头数据放入buf中,以便后续处理
iv.清除原exec环境
  1.线程处理
    a.发送SIGKILL到线程组其它线程,杀死除自己以外所有的线程
    b.如果自己不是主线程,继承主线程信息并将自己设置成主线程
    c.删除信号时钟,并清空时钟信号
    d.如果信号处理handler表与其它进程共享,则创建并复制自己的handler表
  2.将可执行文件句柄设置成新的文件句柄
  3.切换地址空间,使用新的地址空间
v.设置新exec环境
  1.设置进程名称
  2.更新信号handler表,非忽略信号设置成行为,并清空mask
  3.关闭有FD_CLOEXEC标识的文件
vi. 堆栈内存区最后处理,更新内存区标识、权限,内存区可能重定位和扩容
vii.将ELF文件中PT_LOAD类型的程序段映射到内存中
  1.根据PT_LOAD类型的程序头信息,将可执行文件中代码段、数据段等通过文件映射方式映射到内存
  2.PT_LOAD一般包括代码段和数据段,数据段虚拟内存大小通常大于文件大小,多出的部分包括bss内存空间等;采用文件映射且程序段虚拟内存大小大于文件大小时,将多出部分清0(最后一个程序段中,多出部分通常采用匿名映射)
  3.设置start_code,end_code,start_data,end_data,elf_bss,elf_brk;一般情况,elf_bss=数据段虚拟地址+文件大小,elf_brk=数据段虚拟地址+虚拟内存大小
viii.对elf_bss与elf_brk之间的部分(BSS等)作匿名映射;
ix.设置exec返回用户空间后,执行代码的地址;如果有解析器(可执行程序通常是/lib/ld-linux.so.2,ld-linux.so.2作用是装载可执行文件所需的动态链接库),装载解析器,并将程序入口设置成解析器的入口;否则设置成镜像的入口点。
x.设置elf_table
  1.将main参数个数,参数字符串地址,NULL写到堆栈中
  2.将环境变量字符串地址,NULL写到堆栈中
  3.设置elf表,表项为(id,val)对;如程序的入口地址等 
xi.设置进程参数,如IP更改为新程序入口地址,堆栈切换;然后返回用户空间执行解析器代码或程序代码


ELF格式文件处理的实现fs/binfmt_elf.c:

563 static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 564 {
 565         struct file *interpreter = NULL; /* to shut gcc up */
 566         unsigned long load_addr = 0, load_bias = 0;
 567         int load_addr_set = 0;
 568         char * elf_interpreter = NULL;
 569         unsigned long error;
 570         struct elf_phdr *elf_ppnt, *elf_phdata;
 571         unsigned long elf_bss, elf_brk;
 572         int retval, i;
 573         unsigned int size;
 574         unsigned long elf_entry;
 575         unsigned long interp_load_addr = 0;
 576         unsigned long start_code, end_code, start_data, end_data;
 577         unsigned long reloc_func_desc 
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值