由bash:cve-2014-6271 学习execve

我们先了解一下execve:

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

函数说明:
execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,最后一个参数则为传递给执行文件的新环境变量数组。
返回值:
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno 中。
这个代码会执行/bin/sh 使用参数-c 和 env 。 在当前的环境PATH中,运行env环境的程序,该环境变量包含了5个values。执行后的 结果为:

[test@localhost execve-test]$ ./execve-test 
USER=test
PATH=/bin:/usr/bin
PWD=/home/test/execve-test
TZ=UTC0
SHLVL=1
HOME=/
LOGNAME=qfong
_=/bin/env

该段也包含了”_”,”SHLVL”,”PWD”环境变量。
我们调试一下该程序运行的信息

[root@localhost execve]# cat test.c 
#include <unistd.h>
#include <stdio.h>

int main()
{

    char *envp[] = {
    "PATH=/bin:/usr/bin",
    "/usr/bin/id=() { "
    "echo pwn me twice,shame on me; }; "
    "echo pwn me once,shame on you",
    NULL
    };
    char *argv[] = {"/bin/bash",NULL};

    execve(argv[0],argv,envp);
    perror("execve");
return 1
}

测试:

[root@localhost execve]# ./a.out 
pwn me once,shame on you
bash: [: pwn me twice,shame on me: integer expression expected
[root@localhost execve]# /usr/bin/id
pwn me twice,shame on me

使用gdb进行分析一下

[test@localhost execve-test]$ gcc -static -o execve-test-static execve-test.c -O2 
/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status

(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000400a60 <+0>:     sub    $0x58,%rsp
   0x0000000000400a64 <+4>:     mov    $0x48e9b0,%edi
   0x0000000000400a69 <+9>:     lea    0x20(%rsp),%rdx
   0x0000000000400a6e <+14>:    mov    %rsp,%rsi
   0x0000000000400a71 <+17>:    movq   $0x48e9b0,(%rsp)
   0x0000000000400a79 <+25>:    movq   $0x48e9b8,0x8(%rsp)
   0x0000000000400a82 <+34>:    movq   $0x48e9bb,0x10(%rsp)
   0x0000000000400a8b <+43>:    movq   $0x0,0x18(%rsp)
   0x0000000000400a94 <+52>:    movq   $0x48e9bf,0x20(%rsp)
   0x0000000000400a9d <+61>:    movq   $0x48e9c6,0x28(%rsp)
   0x0000000000400aa6 <+70>:    movq   $0x48e9d9,0x30(%rsp)
   0x0000000000400aaf <+79>:    movq   $0x48e9e1,0x38(%rsp)
   0x0000000000400ab8 <+88>:    movq   $0x48e9eb,0x40(%rsp)
   0x0000000000400ac1 <+97>:    movq   $0x0,0x48(%rsp)
   0x0000000000400aca <+106>:   callq  0x40ee50 <execve>
   0x0000000000400acf <+111>:   mov    0x2b75ca(%rip),%rcx        # 0x6b80a0 <stderr>
   0x0000000000400ad6 <+118>:   mov    $0x6,%edx
   0x0000000000400adb <+123>:   mov    $0x1,%esi
   0x0000000000400ae0 <+128>:   mov    $0x48e9f9,%edi
   0x0000000000400ae5 <+133>:   callq  0x401820 <fwrite>
   0x0000000000400aea <+138>:   mov    $0xffffffff,%eax
   0x0000000000400aef <+143>:   add    $0x58,%rsp
   0x0000000000400af3 <+147>:   retq   
End of assembler dump.
(gdb)
(gdb) printf "%s\n", 0x48e9b0
/bin/sh
(gdb) printf "%s\n", 0x48e9b8
-c
(gdb) printf "%s\n", 0x48e9bb
env
(gdb) printf "%s\n", 0x48e9bf
HOME=/
(gdb) printf "%s\n", 0x48e9c6
PATH=/bin:/usr/bin
(gdb) printf "%s\n", 0x48e9d9
TZ=UTC0
(gdb) printf "%s\n", 0x48e9e1
USER=test
(gdb) printf "%s\n", 0x48e9eb
LOGNAME=qfong
(gdb) disassemble 0x40ee50
Dump of assembler code for function execve:
   0x000000000040ee50 <+0>:     mov    $0x3b,%eax //0x3b中断调用号
   0x000000000040ee55 <+5>:     syscall 
   0x000000000040ee57 <+7>:     cmp    $0xfffffffffffff000,%rax
   0x000000000040ee5d <+13>:    ja     0x40ee61 <execve+17>
   0x000000000040ee5f <+15>:    repz retq 
   0x000000000040ee61 <+17>:    mov    $0xffffffffffffffc0,%rdx
   0x000000000040ee68 <+24>:    neg    %eax
   0x000000000040ee6a <+26>:    mov    %eax,%fs:(%rdx)
   0x000000000040ee6d <+29>:    or     $0xffffffffffffffff,%rax
   0x000000000040ee71 <+33>:    retq   
End of assembler dump.
	0x48	0
	0x40	LOGNAME=qfong
	0x38	USER=test
	0x30	TZ=UTC0
	0x28	PATH=/bin:/usr/bin
	0x20	HOME=/
	0x18	0
	0x10	env
	0x8	-c
%rsp	0x4006e0	/bin/sh

Execve()需要各种参数
将数据存放在堆栈中,表示要执行的命令/bin/sh在0x0.依次将参数存入堆栈中。第一个参数必须是该程序的名字mov $0x48e9b0,%edi。Edi为第一参数寄存器。Rsi指向第二寄存器。
在execve执行程序时,我们需要/bin/sh的字符串,一个指针指向这个字符串,和一个指向参数的指针。我们可以看到execve()调用之前的数据表示,一个指向/bin/sh字符串后跟一个指向参数的指针数组(堆栈所在的rsi寄存器),其他的ecx和edx分别为第三第四参数寄存器。

Execve(name[0],name, envp))
Execve 是怎么执行的呢?我们看一下内核里面的代码实现

execve
经过了

sys_execve()  →  do_execve()  → do_execve_common() 
 versus compat_sys_execve() →  compat_do_execve() →  do_execve_common() 这个过程。

可以分析内核的一段代码:

Fs/exec.c
1674 SYSCALL_DEFINE3(execve,
1675 ▸       ▸       const char __user *, filename,
1676 ▸       ▸       const char __user *const __user *, argv,
1677 ▸       ▸       const char __user *const __user *, envp)
1678 {
1679 ▸       struct filename *path = getname(filename); //将一个可执行文件的名称装入到一个新分配的页面中。
1680 ▸       int error = PTR_ERR(path);
1681 ▸       if (!IS_ERR(path)) {
1682 ▸       ▸       error = do_execve(path->name, argv, envp); //执行可执行文件
1683 ▸       ▸       putname(path);
1684 ▸       }
1685 ▸       return error;
1686 }
在这里1674-1677这段代码的SYSCALL_DEFINE3(execve,filename,argv.envp)等同于:
= SYSCALL_DEFINEx(3,## execve,__VA_ARGS)
= asmlinkage long sys_execve(SC_DECL,(_VA_ARGS__))…
= asmlinkage long sys_execve(filename,argv.envp)
参照include/linux/syscalls.h
176 #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

186 #define __SYSCALL_DEFINEx(x, name, ...)▸▸       ▸       ▸       ▸       \
187 ▸       asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));▸     \
188 ▸       static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));▸ \
189 ▸       asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))▸      \
190 ▸       {▸      ▸       ▸       ▸       ▸       ▸       ▸       ▸       \
191 ▸       ▸       long ret = SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));▸ \
192 ▸       ▸       __MAP(x,__SC_TEST,__VA_ARGS__);▸▸       ▸       ▸       \
193 ▸       ▸       __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));▸      \
194 ▸       ▸       return ret;▸    ▸       ▸       ▸       ▸       ▸       \
195 ▸       }▸      ▸       ▸       ▸       ▸       ▸       ▸       ▸       \
196 ▸       SYSCALL_ALIAS(sys##name, SyS##name);▸   ▸       ▸       ▸       \
197 ▸       static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
198
=>
99 #define __SC_DECL(t, a)▸t a


do_execve code:
1580 int do_execve(const char *filename,
1581 ▸       const char __user *const __user *__argv,
1582 ▸       const char __user *const __user *__envp)
1583 {
1584 ▸       struct user_arg_ptr argv = { .ptr.native = __argv }; //封装结构
1585 ▸       struct user_arg_ptr envp = { .ptr.native = __envp }; 
1586 ▸       return do_execve_common(filename, argv, envp); //调用do_execve_common
1587 }
1588


1450 /*
1451  * sys_execve() executes a new program.
1452  */
1453 static int do_execve_common(const char *filename,
1454 ▸       ▸       ▸       ▸       struct user_arg_ptr argv,
1455 ▸       ▸       ▸       ▸       struct user_arg_ptr envp)
1456 {
1457 ▸       struct linux_binprm *bprm; //保存和要执行的文件相关数据
1458 ▸       struct file *file;
1459 ▸       struct files_struct *displaced;
1460 ▸       bool clear_in_exec;
1461 ▸       int retval;
1462 ▸       const struct cred *cred = current_cred();
1463 
1464 ▸       /*
1465 ▸        * We move the actual failure in case of RLIMIT_NPROC excess from
1466 ▸        * set*uid() to execve() because too many poorly written programs
1467 ▸        * don't check setuid() return code.  Here we additionally recheck
1468 ▸        * whether NPROC limit is still exceeded.
1469 ▸        */
1470 ▸       if ((current->flags & PF_NPROC_EXCEEDED) &&
1471 ▸           atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) {
1472 ▸       ▸       retval = -EAGAIN;
1473 ▸       ▸       goto out_ret;
1474 ▸       }
1475 
1476 ▸       /* We're below the limit (still or again), so we don't want to make
1477 ▸        * further execve() calls fail. */
1478 ▸       current->flags &= ~PF_NPROC_EXCEEDED;
1479 
1480 ▸       retval = unshare_files(&displaced);
1481 ▸       if (retval)
1482 ▸       ▸       goto out_ret;
1483 
1484 ▸       retval = -ENOMEM;
1485 ▸       bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); //在堆上分配一个linux_binprm结构
1486 ▸       if (!bprm)
1487 ▸       ▸       goto out_files;
1488 
1489 ▸       retval = prepare_bprm_creds(bprm);
1490 ▸       if (retval)
1491 ▸       ▸       goto out_free;
1492 
1493 ▸       retval = check_unsafe_exec(bprm);
1494 ▸       if (retval < 0)
1495 ▸       ▸       goto out_free;
1496 ▸       clear_in_exec = retval;
1497 ▸       current->in_execve = 1;
1498 
1499 ▸       file = open_exec(filename); //调用open_exec 读取可执行文件
1500 ▸       retval = PTR_ERR(file);
1501 ▸       if (IS_ERR(file))
1502 ▸       ▸       goto out_unmark;
1503 
1504 ▸       sched_exec(); //选择确定最小负载的cpu执行,并把当前process转移过去 
1506 ▸       bprm->file = file; 
1507 ▸       bprm->filename = filename;
1508 ▸       bprm->interp = filename;
1509 
1510 ▸       retval = bprm_mm_init(bprm); //调用bprm_mm_init 为新程序初始化内存
1511 ▸       if (retval)
1512 ▸       ▸       goto out_file;
1513 
1514 ▸       bprm->argc = count(argv, MAX_ARG_STRINGS);
1515 ▸       if ((retval = bprm->argc) < 0)
1516 ▸       ▸       goto out;
1517 
1518 ▸       bprm->envc = count(envp, MAX_ARG_STRINGS);
1519 ▸       if ((retval = bprm->envc) < 0)
1520 ▸       ▸       goto out;
1521 
1522 ▸       retval = prepare_binprm(bprm); 填充bprm结构
1523 ▸       if (retval < 0)
1524 ▸       ▸       goto out;
1525 
1526 ▸       retval = copy_strings_kernel(1, &bprm->filename, bprm); //拷贝filename到新进程中
1527 ▸       if (retval < 0)
1528 ▸       ▸       goto out;
1529 
1530 ▸       bprm->exec = bprm->p;
1531 ▸       retval = copy_strings(bprm->envc, envp, bprm); //拷贝环境变量envp到新进程中

1532 ▸       if (retval < 0)
1533 ▸       ▸       goto out;
1534
1535 ▸       retval = copy_strings(bprm->argc, argv, bprm); //拷贝参数argv到新进程中
1536 ▸       if (retval < 0)
1537 ▸       ▸       goto out;
1538 
1539 ▸       retval = search_binary_handler(bprm); //该函数式对formats链表进行扫描,并尝试每个load_binary函数,如果成功加载了文件的执行格式,对formats的扫描终止
1540 ▸       if (retval < 0)
1541 ▸       ▸       goto out;
1542 
1543 ▸       /* execve succeeded */
1544 ▸       current->fs->in_exec = 0;
1545 ▸       current->in_execve = 0;
1546 ▸       acct_update_integrals(current);
1547 ▸       task_numa_free(current);
1548 ▸       free_bprm(bprm); //释放bprm 结构
1549 ▸       if (displaced)
1550 ▸       ▸       put_files_struct(displaced);
1551 ▸       return retval;
1552 
1553 out:
1554 ▸       if (bprm->mm) {
1555 ▸       ▸       acct_arg_size(bprm, 0);
1556 ▸       ▸       mmput(bprm->mm);
1557 ▸       }
1558 
1559 out_file:
1560 ▸       if (bprm->file) {
1561 ▸       ▸       allow_write_access(bprm->file);
1562 ▸       ▸       fput(bprm->file);
1563 ▸       }
1564 
1565 out_unmark:
1566 ▸       if (clear_in_exec)
1567 ▸       ▸       current->fs->in_exec = 0;
1568 ▸       current->in_execve = 0;
1569 
1570 out_free:
1571 ▸       free_bprm(bprm);
1572 
1573 out_files:
1574 ▸       if (displaced)
1575 ▸       ▸       reset_files_struct(displaced);
1576 out_ret:
1577 ▸       return retval;
1578 }
1579

load_elf_binary函数,该函数位于fs/binfmt_elf.c中
569 static int load_elf_binary(struct linux_binprm *bprm)
 570 {
 571 ▸       struct file *interpreter = NULL; /* to shut gcc up */
 572  ▸      unsigned long load_addr = 0, load_bias = 0;
 573 ▸       int load_addr_set = 0;
 574 ▸       char * elf_interpreter = NULL;
 575 ▸       unsigned long error;
 576 ▸       struct elf_phdr *elf_ppnt, *elf_phdata;
 577 ▸       unsigned long elf_bss, elf_brk;
 578 ▸       int retval, i;
 579 ▸       unsigned int size;
 580 ▸       unsigned long elf_entry;
 581 ▸       unsigned long interp_load_addr = 0;
 582 ▸       unsigned long start_code, end_code, start_data, end_data;
 583 ▸       unsigned long reloc_func_desc __maybe_unused = 0;
 584 ▸       int executable_stack = EXSTACK_DEFAULT;
 585 ▸       unsigned long def_flags = 0;
 586 ▸       struct pt_regs *regs = current_pt_regs();
 587 ▸       struct {
 588 ▸       ▸       struct elfhdr elf_ex;
 589 ▸       ▸       struct elfhdr interp_elf_ex;
 590 ▸       } *loc;
 591 
 592 ▸       loc = kmalloc(sizeof(*loc), GFP_KERNEL);
 593 ▸       if (!loc) {
 594 ▸       ▸       retval = -ENOMEM;
 595 ▸       ▸       goto out_ret;
 596 ▸       }
 597 ▸       
 598 ▸       /* Get the exec-header */
             //读取可执行文件的首部,首部描述程序的段和所需 的共享库
 599 ▸       loc->elf_ex = *((struct elfhdr *)bprm->buf);
 600
600 
 601 ▸       retval = -ENOEXEC;
 602 ▸       /* First of all, some simple consistency checks */ 
             //一致性检测
 603 ▸       if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
 604 ▸       ▸       goto out;
 605 
 606 ▸       if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
 607 ▸       ▸       goto out;
 608 ▸       if (!elf_check_arch(&loc->elf_ex))
 609 ▸       ▸       goto out;
 610 ▸       if (!bprm->file->f_op || !bprm->file->f_op->mmap)
 611 ▸       ▸       goto out;
 612 
 613 ▸       /* Now read in all of the header information */
              //读取所有的首部信息
 614 ▸       if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
 615 ▸       ▸       goto out;
 616 ▸       if (loc->elf_ex.e_phnum < 1 ||
 617 ▸        ▸      loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
 618 ▸       ▸       goto out;
 619 ▸       size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
 620 ▸       retval = -ENOMEM;
 621 ▸       elf_phdata = kmalloc(size, GFP_KERNEL);
 622 ▸       if (!elf_phdata)
 623 ▸       ▸       goto out;
 624 
 625 ▸       retval = kernel_read(bprm->file, loc->elf_ex.e_phoff,
 626 ▸       ▸       ▸            (char *)elf_phdata, size);
 627 ▸       if (retval != size) {
 628 ▸       ▸       if (retval >= 0)
 629 ▸       ▸       ▸       retval = -EIO;
 630 ▸       ▸       goto out_free_ph;
 631 ▸       }
632 
 633 ▸       elf_ppnt = elf_phdata;
 634 ▸       elf_bss = 0;
 635 ▸       elf_brk = 0;
 636 
 637 ▸       start_code = ~0UL;
 638 ▸       end_code = 0;
 639 ▸       start_data = 0;
 640 ▸       end_data = 0;
 641         //从可执行文件中确定动态链接程序的路径名。并用它来确定共享库的位置,并把他们映射到内存。
 642 ▸       for (i = 0; i < loc->elf_ex.e_phnum; i++) {
 643 ▸       ▸       if (elf_ppnt->p_type == PT_INTERP) {
 644 ▸       ▸       ▸       /* This is the program interpreter used for
 645 ▸       ▸       ▸        * shared libraries - for now assume that this
 646 ▸       ▸       ▸        * is an a.out format binary
 647 ▸       ▸       ▸        */
 648 ▸       ▸       ▸       retval = -ENOEXEC;
 649 ▸       ▸       ▸       if (elf_ppnt->p_filesz > PATH_MAX ||▫
 650 ▸       ▸       ▸           elf_ppnt->p_filesz < 2)
 651 ▸       ▸       ▸       ▸       goto out_free_ph;
 652 
 653 ▸       ▸       ▸       retval = -ENOMEM;
 654 ▸       ▸       ▸       elf_interpreter = kmalloc(elf_ppnt->p_filesz,
 655 ▸       ▸       ▸       ▸       ▸       ▸         GFP_KERNEL);
 656 ▸       ▸       ▸       if (!elf_interpreter)
 657 ▸       ▸       ▸       ▸       goto out_free_ph;
 658 
 659 ▸       ▸       ▸       retval = kernel_read(bprm->file, elf_ppnt->p_offset,
 660 ▸       ▸       ▸       ▸       ▸            elf_interpreter,
 661 ▸       ▸       ▸       ▸       ▸            elf_ppnt->p_filesz);
 662 ▸       ▸       ▸       if (retval != elf_ppnt->p_filesz) {
 663 ▸       ▸       ▸       ▸       if (retval >= 0)
 664 ▸       ▸       ▸       ▸       ▸       retval = -EIO;
 665 ▸       ▸       ▸       ▸       goto out_free_interp;
 666 ▸       ▸       ▸       }
667 ▸       ▸       ▸       /* make sure path is NULL terminated */
 668 ▸       ▸       ▸       retval = -ENOEXEC;
 669 ▸       ▸       ▸       if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
 670 ▸       ▸       ▸       ▸       goto out_free_interp;
 671 
 672 ▸       ▸       ▸       interpreter = open_exec(elf_interpreter);
 673 ▸       ▸       ▸       retval = PTR_ERR(interpreter);
 674 ▸       ▸       ▸       if (IS_ERR(interpreter))
 675 ▸       ▸       ▸       ▸       goto out_free_interp;
 676 
 677 ▸       ▸       ▸       /*
 678 ▸       ▸       ▸        * If the binary is not readable then enforce
 679 ▸       ▸       ▸        * mm->dumpable = 0 regardless of the interpreter's
 680 ▸       ▸       ▸        * permissions.
 681 ▸       ▸       ▸        */
 682 ▸       ▸       ▸       would_dump(bprm, interpreter);
 683 
 684 ▸       ▸       ▸       retval = kernel_read(interpreter, 0, bprm->buf,
 685 ▸       ▸       ▸       ▸       ▸            BINPRM_BUF_SIZE);
 686 ▸       ▸       ▸       if (retval != BINPRM_BUF_SIZE) {
 687 ▸       ▸       ▸       ▸       if (retval >= 0)
 688 ▸       ▸       ▸       ▸       ▸       retval = -EIO;
 689 ▸       ▸       ▸       ▸       goto out_free_dentry;
 690 ▸       ▸       ▸       }
 691 
 692 ▸       ▸       ▸       /* Get the exec headers */
 693 ▸       ▸       ▸       loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
 694 ▸       ▸       ▸       break;
 695 ▸       ▸       }
 696 ▸       ▸       elf_ppnt++;
 697 ▸       }
698 
 699 ▸       elf_ppnt = elf_phdata;
 700 ▸       for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
 701 ▸       ▸       if (elf_ppnt->p_type == PT_GNU_STACK) {
 702 ▸       ▸       ▸       if (elf_ppnt->p_flags & PF_X)
 703 ▸       ▸       ▸       ▸       executable_stack = EXSTACK_ENABLE_X;
 704 ▸       ▸       ▸       else
 705 ▸       ▸       ▸       ▸       executable_stack = EXSTACK_DISABLE_X;
 706 ▸       ▸       ▸       break;
 707 ▸       ▸       }
 708 
 709 ▸       /* Some simple consistency checks for the interpreter */
             //对动态链接库执行一致性检测
 710 ▸       if (elf_interpreter) {
 711 ▸       ▸       retval = -ELIBBAD;
 712 ▸       ▸       /* Not an ELF interpreter */
 713 ▸       ▸       if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
 714 ▸       ▸       ▸       goto out_free_dentry;
 715 ▸       ▸       /* Verify the interpreter has a valid arch */
 716 ▸       ▸       if (!elf_check_arch(&loc->interp_elf_ex))
 717 ▸       ▸       ▸       goto out_free_dentry;
 718 ▸       }
 719 
 720 ▸       /* Flush all traces of the currently running executable */
              //释放当前运行的可执行文件占用的所有资源
 721 ▸       retval = flush_old_exec(bprm);
 722 ▸       if (retval)
 723 ▸       ▸       goto out_free_dentry;
 724 
 725 ▸       /* OK, This is the point of no return */
 726 ▸       current->mm->def_flags = def_flags;
 727 
 728 ▸       /* Do this immediately, since STACK_TOP as used in setup_arg_pages
 729 ▸          may depend on the personality.  */
 730 ▸       SET_PERSONALITY(loc->elf_ex);
731 ▸       if (elf_read_implies_exec(loc->elf_ex, executable_stack))
 732 ▸       ▸       current->personality |= READ_IMPLIES_EXEC;
 733 
 734 ▸       if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
 735 ▸       ▸       current->flags |= PF_RANDOMIZE;
 736 
 737 ▸       setup_new_exec(bprm);
 738 
 739 ▸       /* Do this so that we can load the interpreter, if need be.  We will
 740 ▸          change some of these later */
 741 ▸       current->mm->free_area_cache = current->mm->mmap_base;
 742 ▸       current->mm->cached_hole_size = 0;
            //为进程的用户态堆栈分配一个新的线性区描述符,并把那个线性区插入到进程的地址空间。
 743 ▸       retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
 744 ▸       ▸       ▸       ▸        executable_stack);
 745 ▸       if (retval < 0) {
 746 ▸       ▸       send_sig(SIGKILL, current, 0);
 747 ▸       ▸       goto out_free_dentry;
 748 ▸       }
 749 ▸       
 750 ▸       current->mm->start_stack = bprm->p;
 751 
 752 ▸       /* Now we do a little grungy work by mmapping the ELF image into
 753 ▸          the correct location in memory. */
            //将ELF镜像文件映射到内存中正确的位置
 754 ▸       for(i = 0, elf_ppnt = elf_phdata;
 755 ▸           i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
 756 ▸       ▸       int elf_prot = 0, elf_flags;
 757 ▸       ▸       unsigned long k, vaddr;
 758 
 759 ▸       ▸       if (elf_ppnt->p_type != PT_LOAD)
 760 ▸       ▸       ▸       continue;
 761 
 762 ▸       ▸       if (unlikely (elf_brk > elf_bss)) {
 763 ▸       ▸       ▸       unsigned long nbyte;
764 ▸       ▫▫▫▫▫▫▫▫▫▫▫▫
 765 ▸       ▸       ▸       /* There was a PT_LOAD segment with p_memsz > p_filesz
 766 ▸       ▸       ▸          before this one. Map anonymous pages, if needed,
 767 ▸       ▸       ▸          and clear the area.  */
 768 ▸       ▸       ▸       retval = set_brk(elf_bss + load_bias,
 769 ▸       ▸       ▸       ▸       ▸        elf_brk + load_bias);
 770 ▸       ▸       ▸       if (retval) {
 771 ▸       ▸       ▸       ▸       send_sig(SIGKILL, current, 0);
 772 ▸       ▸       ▸       ▸       goto out_free_dentry;
 773 ▸       ▸       ▸       }
 774 ▸       ▸       ▸       nbyte = ELF_PAGEOFFSET(elf_bss);
 775 ▸       ▸       ▸       if (nbyte) {
 776 ▸       ▸       ▸       ▸       nbyte = ELF_MIN_ALIGN - nbyte;
 777 ▸       ▸       ▸       ▸       if (nbyte > elf_brk - elf_bss)
 778 ▸       ▸       ▸       ▸       ▸       nbyte = elf_brk - elf_bss;
 779 ▸       ▸       ▸       ▸       if (clear_user((void __user *)elf_bss +
 780 ▸       ▸       ▸       ▸       ▸       ▸       ▸       load_bias, nbyte)) {
 781 ▸       ▸       ▸       ▸       ▸       /*
 782 ▸       ▸       ▸       ▸       ▸        * This bss-zeroing can fail if the ELF
 783 ▸       ▸       ▸       ▸       ▸        * file specifies odd protections. So
 784 ▸       ▸       ▸       ▸       ▸        * we don't check the return value
 785 ▸       ▸       ▸       ▸       ▸        */
 786 ▸       ▸       ▸       ▸       }
 787 ▸       ▸       ▸       }
 788 ▸       ▸       }
 789 
 790 ▸       ▸       if (elf_ppnt->p_flags & PF_R)
 791 ▸       ▸       ▸       elf_prot |= PROT_READ;
 792 ▸       ▸       if (elf_ppnt->p_flags & PF_W)
 793 ▸       ▸       ▸       elf_prot |= PROT_WRITE;
 794 ▸       ▸       if (elf_ppnt->p_flags & PF_X)
 795 ▸       ▸       ▸       elf_prot |= PROT_EXEC;
796 
 797 ▸       ▸       elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
 798 
 799 ▸       ▸       vaddr = elf_ppnt->p_vaddr;
 800 ▸       ▸       if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
 801 ▸       ▸       ▸       elf_flags |= MAP_FIXED;
 802 ▸       ▸       } else if (loc->elf_ex.e_type == ET_DYN) {
 803 ▸       ▸       ▸       /* Try and get dynamic programs out of the way of the
 804 ▸       ▸       ▸        * default mmap base, as well as whatever program they
 805 ▸       ▸       ▸        * might try to exec.  This is because the brk will
 806 ▸       ▸       ▸        * follow the loader, and is not movable.  */
 807 #ifdef CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE
 808 ▸       ▸       ▸       /* Memory randomization might have been switched off
 809 ▸       ▸       ▸        * in runtime via sysctl or explicit setting of
 810 ▸       ▸       ▸        * personality flags.
 811 ▸       ▸       ▸        * If that is the case, retain the original non-zero
 812 ▸       ▸       ▸        * load_bias value in order to establish proper
 813 ▸       ▸       ▸        * non-randomized mappings.
 814 ▸       ▸       ▸        */
 815 ▸       ▸       ▸       if (current->flags & PF_RANDOMIZE)
 816 ▸       ▸       ▸       ▸       load_bias = 0;
 817 ▸       ▸       ▸       else
 818 ▸       ▸       ▸       ▸       load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
 819 #else
 820 ▸       ▸       ▸       load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
 821 #endif
 822 ▸       ▸       }
 823
824 ▸       ▸       error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
 825 ▸       ▸       ▸       ▸       elf_prot, elf_flags, 0);
 826 ▸       ▸       if (BAD_ADDR(error)) {
 827 ▸       ▸       ▸       send_sig(SIGKILL, current, 0);
 828 ▸       ▸       ▸       retval = IS_ERR((void *)error) ?
 829 ▸       ▸       ▸       ▸       PTR_ERR((void*)error) : -EINVAL;
 830 ▸       ▸       ▸       goto out_free_dentry;
 831 ▸       ▸       }
 832 
 833 ▸       ▸       if (!load_addr_set) {
 834 ▸       ▸       ▸       load_addr_set = 1;
 835 ▸       ▸       ▸       load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
 836 ▸       ▸       ▸       if (loc->elf_ex.e_type == ET_DYN) {
 837 ▸       ▸       ▸       ▸       load_bias += error -
 838 ▸       ▸       ▸       ▸                    ELF_PAGESTART(load_bias + vaddr);
 839 ▸       ▸       ▸       ▸       load_addr += load_bias;
 840 ▸       ▸       ▸       ▸       reloc_func_desc = load_bias;
 841 ▸       ▸       ▸       }
 842 ▸       ▸       }
 843 ▸       ▸       k = elf_ppnt->p_vaddr;
 844 ▸       ▸       if (k < start_code)
 845 ▸       ▸       ▸       start_code = k;
 846 ▸       ▸       if (start_data < k)
 847 ▸       ▸       ▸       start_data = k;
 848 
 849 ▸       ▸       /*
 850 ▸       ▸        * Check to see if the section's size will overflow the
 851 ▸       ▸        * allowed task size. Note that p_filesz must always be
 852 ▸       ▸        * <= p_memsz so it is only necessary to check p_memsz.
 853 ▸       ▸        */
 854 ▸       ▸       if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
 855 ▸       ▸           elf_ppnt->p_memsz > TASK_SIZE ||
 856 ▸       ▸           TASK_SIZE - elf_ppnt->p_memsz < k) {
857 ▸       ▸       ▸       /* set_brk can never work. Avoid overflows. */
 858 ▸       ▸       ▸       send_sig(SIGKILL, current, 0);
 859 ▸       ▸       ▸       retval = -EINVAL;
 860 ▸       ▸       ▸       goto out_free_dentry;
 861 ▸       ▸       }
 862 
 863 ▸       ▸       k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
 864 
 865 ▸       ▸       if (k > elf_bss)
 866 ▸       ▸       ▸       elf_bss = k;
 867 ▸       ▸       if ((elf_ppnt->p_flags & PF_X) && end_code < k)
 868 ▸       ▸       ▸       end_code = k;
 869 ▸       ▸       if (end_data < k)
 870 ▸       ▸       ▸       end_data = k;
 871 ▸       ▸       k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
 872 ▸       ▸       if (k > elf_brk)
 873 ▸       ▸       ▸       elf_brk = k;
 874 ▸       }
 875 
 876 ▸       loc->elf_ex.e_entry += load_bias;
 877 ▸       elf_bss += load_bias;
 878 ▸       elf_brk += load_bias;
 879 ▸       start_code += load_bias;
 880 ▸       end_code += load_bias;
 881 ▸       start_data += load_bias;
 882 ▸       end_data += load_bias;
 883 
 884 ▸       /* Calling set_brk effectively mmaps the pages that we need
 885 ▸        * for the bss and break sections.  We must do this before
 886 ▸        * mapping in the interpreter, to make sure it doesn't wind
 887 ▸        * up getting placed where the bss needs to go.
 888 ▸        */
890 ▸       if (retval) {
 891 ▸       ▸       send_sig(SIGKILL, current, 0);
 892 ▸       ▸       goto out_free_dentry;
 893 ▸       }
 894 ▸       if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) {
 895 ▸       ▸       send_sig(SIGSEGV, current, 0);
 896 ▸       ▸       retval = -EFAULT; /* Nobody gets to see this, but.. */
 897 ▸       ▸       goto out_free_dentry;
 898 ▸       }
 899          //调用一个动态链接程序的函数,如果动态链接程序是elf可执行的,调用load_elf_interp(动态链接程序)
 900 ▸       if (elf_interpreter) {
 901 ▸       ▸       unsigned long interp_map_addr = 0;
 902 
 903 ▸       ▸       elf_entry = load_elf_interp(&loc->interp_elf_ex,
 904 ▸       ▸       ▸       ▸       ▸           interpreter,
 905 ▸       ▸       ▸       ▸       ▸           &interp_map_addr,
 906 ▸       ▸       ▸       ▸       ▸           load_bias);
 907 ▸       ▸       if (!IS_ERR((void *)elf_entry)) {
 908 ▸       ▸       ▸       /*
 909 ▸       ▸       ▸        * load_elf_interp() returns relocation
 910 ▸       ▸       ▸        * adjustment
 911 ▸       ▸       ▸        */
 912 ▸       ▸       ▸       interp_load_addr = elf_entry;
 913 ▸       ▸       ▸       elf_entry += loc->interp_elf_ex.e_entry;
 914 ▸       ▸       }
 915 ▸       ▸       if (BAD_ADDR(elf_entry)) {
 916 ▸       ▸       ▸       force_sig(SIGSEGV, current);
 917 ▸       ▸       ▸       retval = IS_ERR((void *)elf_entry) ?
 918 ▸       ▸       ▸       ▸       ▸       (int)elf_entry : -EINVAL;
 919 ▸       ▸       ▸       goto out_free_dentry;
 920 ▸       ▸       }
 921 ▸       ▸       reloc_func_desc = interp_load_addr;
 922
923 ▸       ▸       allow_write_access(interpreter);
 924 ▸       ▸       fput(interpreter);
 925 ▸       ▸       kfree(elf_interpreter);
 926 ▸       } else {
 927 ▸       ▸       elf_entry = loc->elf_ex.e_entry;
 928 ▸       ▸       if (BAD_ADDR(elf_entry)) {
 929 ▸       ▸       ▸       force_sig(SIGSEGV, current);
 930 ▸       ▸       ▸       retval = -EINVAL;
 931 ▸       ▸       ▸       goto out_free_dentry;
 932 ▸       ▸       }
 933 ▸       }
 934 
 935 ▸       kfree(elf_phdata);
 936         //把可执行格式的linux_binfmt对象地址存放在进程描述符binfmt字段中
 937 ▸       set_binfmt(&elf_format);
 938 
 939 #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
 940 ▸       retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
 941 ▸       if (retval < 0) {
 942 ▸       ▸       send_sig(SIGKILL, current, 0);
 943 ▸       ▸       goto out;
 944 ▸       }
 945 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
 946 
 947 ▸       install_exec_creds(bprm);
 948 ▸       retval = create_elf_tables(bprm, &loc->elf_ex,
 949 ▸       ▸       ▸         load_addr, interp_load_addr);
 950 ▸       if (retval < 0) {
 951 ▸       ▸       send_sig(SIGKILL, current, 0);
952 ▸       ▸       goto out;
 953 ▸       }
 954 ▸       /* N.B. passed_fileno might not be initialized? */
 955 ▸       current->mm->end_code = end_code;
 956 ▸       current->mm->start_code = start_code;
 957 ▸       current->mm->start_data = start_data;
 958 ▸       current->mm->end_data = end_data;
 959 ▸       current->mm->start_stack = bprm->p;
 960 
 961 #ifdef arch_randomize_brk
 962 ▸       if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
 963 ▸       ▸       current->mm->brk = current->mm->start_brk =
 964 ▸       ▸       ▸       arch_randomize_brk(current->mm);
 965 #ifdef CONFIG_COMPAT_BRK
 966 ▸       ▸       current->brk_randomized = 1;
 967 #endif
 968 ▸       }
 969 #endif
 970 
 971 ▸       if (current->personality & MMAP_PAGE_ZERO) {
 972 ▸       ▸       /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
 973 ▸       ▸          and some applications "depend" upon this behavior.
 974 ▸       ▸          Since we do not have the power to recompile these, we
 975 ▸       ▸          emulate the SVr4 behavior. Sigh. */
 976 ▸       ▸       error = vm_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
 977 ▸       ▸       ▸       ▸       MAP_FIXED | MAP_PRIVATE, 0);
 978 ▸       }
 979 
 980 #ifdef ELF_PLAT_INIT
981 ▸       /*
 982 ▸        * The ABI may specify that certain registers be set up in special
 983 ▸        * ways (on i386 %edx is the address of a DT_FINI function, for
 984 ▸        * example.  In addition, it may also specify (eg, PowerPC64 ELF)
 985 ▸        * that the e_entry field is the address of the function descriptor
 986 ▸        * for the startup routine, rather than the address of the startup
 987 ▸        * routine itself.  This macro performs whatever initialization to
 988 ▸        * the regs structure is required as well as any relocations to the
 989 ▸        * function descriptor entries when executing dynamically links apps.
 990 ▸        */
 991 ▸       ELF_PLAT_INIT(regs, reloc_func_desc);
 992 #endif
 993 
 994 ▸       start_thread(regs, elf_entry, bprm->p);
 995 ▸       retval = 0;
 996 out:
 997 ▸       kfree(loc);
 998 out_ret:
 999 ▸       return retval;
1000 
1001 ▸       /* error cleanup */
1002 out_free_dentry:
1003 ▸       allow_write_access(interpreter);
1004 ▸       if (interpreter)
1005 ▸       ▸       fput(interpreter);
1006 out_free_interp:
1007 ▸       kfree(elf_interpreter);
1008 out_free_ph:
1009 ▸       kfree(elf_phdata);
1010 ▸       goto out;
1011 }

附录:
X86-64中,所有寄存器都是64位,相对32位的x86来说,标识符发生了变化,比如:从原来的%ebp变成了%rbp。为了向后兼容性,%ebp依然可以使用,不过指向了%rbp的低32位。
X86-64寄存器的变化,不仅体现在位数上,更加体现在寄存器数量上。新增加寄存器%r8到%r15。加上x86的原有8个,一共16个寄存器。
刚刚说到,寄存器集成在CPU上,存取速度比存储器快好几个数量级,寄存器多了,GCC就可以更多的使用寄存器,替换之前的存储器堆栈使用,从而大大提升性能。
让寄存器为己所用,就得了解它们的用途,这些用途都涉及函数调用,X86-64有16个64位寄存器,分别是:%rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp,%r8,%r9,%r10,%r11,%r12,%r13,%r14,%r15。其中:
• %rax 作为函数返回值使用。
• %rsp 栈指针寄存器,指向栈顶
• %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
• %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
• %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
x86-64寄存器2

发表评论

您的电子邮箱地址不会被公开。